From 43df9d6aabca0969e4b2a648a177fce6dc605bf7 Mon Sep 17 00:00:00 2001 From: David Clark Date: Tue, 29 Nov 2016 21:38:36 -0700 Subject: [PATCH] Drop babel (#2134) Drop babel --- .eslintignore | 1 - .eslintrc | 15 +- .gitignore | 1 - .travis.yml | 2 +- bin/stylelint | 3 + docs/developer-guide/plugins.md | 2 +- docs/developer-guide/rule-testers.md | 4 +- docs/developer-guide/rules.md | 6 +- docs/user-guide/css-processors.md | 2 +- docs/user-guide/postcss-plugin.md | 4 +- docs/user-guide/rules.md | 344 +- {src => lib}/__tests__/createLinter.test.js | 29 +- .../__tests__/defaultSeverity.test.js | 4 +- .../disableRanges-integration-test.js | 90 +- {src => lib}/__tests__/disableRanges.test.js | 232 +- {src => lib}/__tests__/extends.test.js | 48 +- .../config-at-rule-empty-line-before.json | 0 .../fixtures/config-block-empty-ok.json | 0 .../fixtures/config-block-no-empty.json | 0 .../config-color-named-custom-message.yaml | 0 .../fixtures/config-color-no-invalid-hex.json | 0 .../config-extending-and-ignoring.json | 0 .../config-extending-another-extend.json | 0 .../fixtures/config-extending-one.json | 0 .../config-extending-three-with-override.json | 0 .../fixtures/config-extending-two.json | 0 .../config-extending-with-plugin.json | 0 .../__tests__/fixtures/config-no-pixels.json | 0 .../fixtures/config-per-file/a/.stylelintrc | 0 .../fixtures/config-per-file/a/a.css | 0 .../fixtures/config-per-file/a/b/.stylelintrc | 0 .../fixtures/config-per-file/a/b/b.css | 0 .../config-per-file/a/b/c/.stylelintrc | 0 .../fixtures/config-per-file/a/b/c/c.css | 0 .../fixtures/config-per-file/a/b/c/d/d.css | 0 .../config-plugin-extending-with-plugin.json | 0 .../config-relative-plugin-nested.json | 0 .../fixtures/config-relative-plugin.json | 0 .../fixtures/config-string-quotes-single.json | 0 .../fixtures/config-with-undefined-rule.json | 0 .../fixtures/config-without-rules.json | 0 .../__tests__/fixtures/custom-parser.js | 2 + .../__tests__/fixtures/custom-syntax.js | 2 + .../fixtures/empty-block-with-disables.css | 0 .../__tests__/fixtures/empty-block.css | 0 .../fixtures/extension-sensitive.less | 0 .../fixtures/extension-sensitive.scss | 0 .../fixtures/extension-sensitive.sss | 0 .../fixtures/getConfigForFile/a/.stylelintrc | 0 .../fixtures/getConfigForFile/a/b/foo.css | 0 {src => lib}/__tests__/fixtures/ignore.txt | 0 .../__tests__/fixtures/invalid-hex.css | 0 .../__tests__/fixtures/invalid-hex.html | 0 .../needlessDisables/.stylelintignore | 0 .../needlessDisables/disabled-ranges-1.css | 0 .../needlessDisables/disabled-ranges-2.css | 0 .../needlessDisables/disabled-ranges-3.css | 0 .../needlessDisables/ignored-file.css | 0 .../plugin-nested-warn-about-foo.js | 6 +- lib/__tests__/fixtures/plugin-array.js | 6 + ...ugin-conditionally-check-color-hex-case.js | 6 +- .../fixtures/plugin-primary-array.js | 6 +- .../plugin-slashless-warn-about-foo.js | 6 +- .../fixtures/plugin-warn-about-bar.js | 6 +- .../fixtures/plugin-warn-about-foo.js | 6 +- .../fixtures/processor-fenced-blocks.js | 10 +- .../processor-triple-question-marks.js | 8 +- .../standaloneNoParsing/no-syntax-error.css | 0 .../syntax-error-ignored.scss | 0 {src => lib}/__tests__/ignore.test.js | 50 +- {src => lib}/__tests__/ignoreDisables.test.js | 8 +- {src => lib}/__tests__/integration.test.js | 72 +- lib/__tests__/message.test.js | 14 + lib/__tests__/needlessDisables.test.js | 86 + .../normalizeRuleSettings-integration.test.js | 30 +- .../__tests__/normalizeRuleSettings.test.js | 4 +- {src => lib}/__tests__/plugins.test.js | 61 +- {src => lib}/__tests__/postcssPlugin.test.js | 22 +- {src => lib}/__tests__/processors.test.js | 34 +- .../__tests__/standalone-formatter.test.js | 18 +- .../standalone-needlessDisables.test.js | 14 +- .../__tests__/standalone-quiet.test.js | 8 +- .../__tests__/standalone-syntax.test.js | 54 +- {src => lib}/__tests__/standalone.test.js | 70 +- .../stylelintignore-test/.stylelintignore | 0 .../stylelintignore.test.js | 11 +- {src => lib}/assignDisabledRanges.js | 58 +- {src => lib}/augmentConfig.js | 240 +- {src => lib}/cli.js | 54 +- lib/createPlugin.js | 8 + {src => lib}/createStylelint.js | 32 +- {src => lib}/createStylelintResult.js | 29 +- .../__tests__/jsonFormatter-test.js | 12 +- .../needlessDisablesStringFormatter-test.js | 32 + .../__tests__/prepareFormatterOutput.js | 19 + .../__tests__/stringFormatter-test.js | 52 +- .../__tests__/verboseFormatter-test.js | 52 +- lib/formatters/index.js | 7 + {src => lib}/formatters/jsonFormatter.js | 6 +- .../needlessDisablesStringFormatter.js | 12 +- {src => lib}/formatters/stringFormatter.js | 93 +- {src => lib}/formatters/verboseFormatter.js | 26 +- lib/getConfigForFile.js | 43 + lib/getPostcssResult.js | 97 + lib/index.js | 27 + {src => lib}/isPathIgnored.js | 24 +- {src => lib}/lintSource.js | 80 +- {src => lib}/needlessDisables.js | 45 +- {src => lib}/normalizeRuleSettings.js | 47 +- lib/postcssPlugin.js | 19 + lib/reference/keywordSets.js | 138 + lib/reference/namedColorData.js | 596 ++++ lib/reference/propertySets.js | 7 + lib/reference/punctuationSets.js | 9 + lib/reference/shorthandData.js | 22 + .../rules/at-rule-blacklist/README.md | 0 .../at-rule-blacklist/__tests__/index.js | 18 +- lib/rules/at-rule-blacklist/index.js | 48 + .../rules/at-rule-empty-line-before/README.md | 0 .../__tests__/index.js | 18 +- lib/rules/at-rule-empty-line-before/index.js | 114 + .../rules/at-rule-name-case/README.md | 0 .../at-rule-name-case/__tests__/index.js | 12 +- lib/rules/at-rule-name-case/index.js | 44 + .../at-rule-name-newline-after/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/at-rule-name-newline-after/index.js | 36 + .../rules/at-rule-name-space-after/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/at-rule-name-space-after/index.js | 36 + .../rules/at-rule-no-unknown/README.md | 0 .../at-rule-no-unknown/__tests__/index.js | 12 +- lib/rules/at-rule-no-unknown/index.js | 56 + .../rules/at-rule-no-vendor-prefix/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/at-rule-no-vendor-prefix/index.js | 49 + .../at-rule-semicolon-newline-after/README.md | 0 .../__tests__/index.js | 12 +- .../at-rule-semicolon-newline-after/index.js | 63 + .../rules/at-rule-whitelist/README.md | 0 .../at-rule-whitelist/__tests__/index.js | 18 +- lib/rules/at-rule-whitelist/index.js | 48 + lib/rules/atRuleNameSpaceChecker.js | 29 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 77 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 70 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 54 +- .../block-closing-brace-space-after/README.md | 0 .../__tests__/index.js | 12 +- .../block-closing-brace-space-after/index.js | 49 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../block-closing-brace-space-before/index.js | 49 +- {src => lib}/rules/block-no-empty/README.md | 0 .../rules/block-no-empty/__tests__/index.js | 12 +- {src => lib}/rules/block-no-empty/index.js | 32 +- .../rules/block-no-single-line/README.md | 0 .../block-no-single-line/__tests__/index.js | 12 +- lib/rules/block-no-single-line/index.js | 50 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 52 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 50 +- .../block-opening-brace-space-after/README.md | 0 .../__tests__/index.js | 12 +- .../block-opening-brace-space-after/index.js | 51 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../block-opening-brace-space-before/index.js | 61 +- lib/rules/checkRuleEmptyLineBefore.js | 46 + {src => lib}/rules/color-hex-case/README.md | 0 .../rules/color-hex-case/__tests__/index.js | 16 +- {src => lib}/rules/color-hex-case/index.js | 39 +- {src => lib}/rules/color-hex-length/README.md | 0 .../rules/color-hex-length/__tests__/index.js | 16 +- {src => lib}/rules/color-hex-length/index.js | 49 +- {src => lib}/rules/color-named/README.md | 0 .../rules/color-named/__tests__/index.js | 16 +- lib/rules/color-named/index.js | 124 + {src => lib}/rules/color-no-hex/README.md | 0 .../rules/color-no-hex/__tests__/index.js | 21 +- {src => lib}/rules/color-no-hex/index.js | 34 +- .../rules/color-no-invalid-hex/README.md | 0 .../color-no-invalid-hex/__tests__/index.js | 21 +- .../rules/color-no-invalid-hex/index.js | 40 +- .../rules/comment-empty-line-before/README.md | 0 .../__tests__/index.js | 16 +- lib/rules/comment-empty-line-before/index.js | 91 + {src => lib}/rules/comment-no-empty/README.md | 0 .../rules/comment-no-empty/__tests__/index.js | 12 +- lib/rules/comment-no-empty/index.js | 41 + .../rules/comment-whitespace-inside/README.md | 0 .../__tests__/index.js | 12 +- .../rules/comment-whitespace-inside/index.js | 39 +- .../rules/comment-word-blacklist/README.md | 0 .../comment-word-blacklist/__tests__/index.js | 42 +- lib/rules/comment-word-blacklist/index.js | 56 + .../rules/custom-media-pattern/README.md | 0 .../custom-media-pattern/__tests__/index.js | 12 +- lib/rules/custom-media-pattern/index.js | 51 + .../README.md | 0 .../__tests__/index.js | 24 +- .../index.js | 95 + .../custom-property-no-outside-root/README.md | 0 .../__tests__/index.js | 12 +- .../custom-property-no-outside-root/index.js | 44 + .../rules/custom-property-pattern/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/custom-property-pattern/index.js | 49 + .../declaration-bang-space-after/README.md | 0 .../__tests__/index.js | 12 +- .../declaration-bang-space-after/index.js | 37 + .../declaration-bang-space-before/README.md | 0 .../__tests__/index.js | 12 +- .../declaration-bang-space-before/index.js | 37 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 72 +- .../README.md | 0 .../__tests__/index.js | 42 +- .../index.js | 124 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 80 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 57 + .../README.md | 0 .../__tests__/alphabetical.js | 12 +- .../__tests__/flat.js | 53 +- .../__tests__/grouped-flexible.js | 52 +- .../__tests__/grouped-strict.js | 52 +- .../__tests__/validate-options.js | 93 +- .../index.js | 152 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 54 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 38 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 49 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 39 +- .../README.md | 0 .../__tests__/index.js | 15 +- .../index.js | 54 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 74 + .../declaration-colon-newline-after/README.md | 0 .../__tests__/index.js | 37 +- .../declaration-colon-newline-after/index.js | 51 +- .../declaration-colon-space-after/README.md | 0 .../__tests__/index.js | 12 +- .../declaration-colon-space-after/index.js | 38 + .../declaration-colon-space-before/README.md | 0 .../__tests__/index.js | 12 +- .../declaration-colon-space-before/index.js | 37 + .../declaration-empty-line-before/README.md | 0 .../__tests__/index.js | 24 +- .../declaration-empty-line-before/index.js | 101 + .../rules/declaration-no-important/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/declaration-no-important/index.js | 38 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 70 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 70 + .../README.md | 0 .../__tests__/index.js | 22 +- .../index.js | 53 + .../README.md | 0 .../__tests__/index.js | 22 +- .../index.js | 53 + lib/rules/declarationBangSpaceChecker.js | 31 + lib/rules/declarationColonSpaceChecker.js | 41 + lib/rules/findMediaOperator.js | 15 + .../rules/font-family-name-quotes/README.md | 0 .../__tests__/index.js | 46 +- .../rules/font-family-name-quotes/index.js | 72 +- .../font-family-no-duplicate-names/README.md | 0 .../__tests__/index.js | 12 +- .../font-family-no-duplicate-names/index.js | 41 +- .../rules/font-weight-notation/README.md | 0 .../font-weight-notation/__tests__/index.js | 12 +- .../rules/font-weight-notation/index.js | 78 +- .../rules/function-blacklist/README.md | 0 .../function-blacklist/__tests__/index.js | 18 +- lib/rules/function-blacklist/index.js | 58 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 68 +- .../function-comma-newline-after/README.md | 0 .../__tests__/index.js | 18 +- .../function-comma-newline-after/index.js | 38 + .../function-comma-newline-before/README.md | 0 .../__tests__/index.js | 12 +- .../function-comma-newline-before/index.js | 32 +- .../function-comma-space-after/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/function-comma-space-after/index.js | 39 + .../function-comma-space-before/README.md | 0 .../__tests__/index.js | 12 +- .../function-comma-space-before/index.js | 33 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 54 +- .../rules/function-max-empty-lines/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/function-max-empty-lines/index.js | 63 + .../rules/function-name-case/README.md | 0 .../function-name-case/__tests__/index.js | 12 +- .../rules/function-name-case/index.js | 63 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 54 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 55 +- .../rules/function-url-data-uris/README.md | 0 .../function-url-data-uris/__tests__/index.js | 12 +- lib/rules/function-url-data-uris/index.js | 61 + .../function-url-no-scheme-relative/README.md | 0 .../__tests__/index.js | 12 +- .../function-url-no-scheme-relative/index.js | 45 + .../rules/function-url-quotes/README.md | 0 .../function-url-quotes/__tests__/index.js | 12 +- .../rules/function-url-quotes/index.js | 51 +- .../function-url-scheme-whitelist/README.md | 0 .../__tests__/index.js | 12 +- .../function-url-scheme-whitelist/index.js | 73 + .../rules/function-whitelist/README.md | 0 .../function-whitelist/__tests__/index.js | 20 +- lib/rules/function-whitelist/index.js | 58 + .../rules/function-whitespace-after/README.md | 0 .../__tests__/index.js | 12 +- .../rules/function-whitespace-after/index.js | 49 +- lib/rules/functionCommaSpaceChecker.js | 62 + {src => lib}/rules/indentation/README.md | 0 lib/rules/indentation/__tests__/at-rules.js | 180 + lib/rules/indentation/__tests__/comments.js | 63 + lib/rules/indentation/__tests__/functions.js | 314 ++ lib/rules/indentation/__tests__/rules.js | 367 ++ lib/rules/indentation/__tests__/selectors.js | 96 + {src => lib}/rules/indentation/index.js | 132 +- lib/rules/index.js | 349 ++ .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 39 + .../rules/length-zero-no-unit/README.md | 0 .../length-zero-no-unit/__tests__/index.js | 12 +- .../rules/length-zero-no-unit/index.js | 82 +- {src => lib}/rules/max-empty-lines/README.md | 0 .../rules/max-empty-lines/__tests__/index.js | 12 +- {src => lib}/rules/max-empty-lines/index.js | 47 +- {src => lib}/rules/max-line-length/README.md | 0 .../rules/max-line-length/__tests__/index.js | 12 +- {src => lib}/rules/max-line-length/index.js | 52 +- .../rules/max-nesting-depth/README.md | 0 .../max-nesting-depth/__tests__/index.js | 14 +- {src => lib}/rules/max-nesting-depth/index.js | 52 +- .../media-feature-colon-space-after/README.md | 0 .../__tests__/index.js | 12 +- .../media-feature-colon-space-after/index.js | 37 + .../README.md | 0 .../__tests__/index.js | 12 +- .../media-feature-colon-space-before/index.js | 37 + .../media-feature-name-blacklist/README.md | 0 .../__tests__/index.js | 29 +- .../media-feature-name-blacklist/index.js | 58 + .../rules/media-feature-name-case/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/media-feature-name-case/index.js | 58 + .../media-feature-name-no-unknown/README.md | 0 .../__tests__/index.js | 12 +- .../media-feature-name-no-unknown/index.js | 68 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 43 + .../media-feature-name-whitelist/README.md | 0 .../__tests__/index.js | 27 +- .../media-feature-name-whitelist/index.js | 58 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 58 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 35 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 44 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 34 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 32 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 37 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 38 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 33 +- lib/rules/mediaFeatureColonSpaceChecker.js | 26 + .../mediaQueryListCommaWhitespaceChecker.js | 25 + {src => lib}/rules/no-browser-hacks/README.md | 0 .../rules/no-browser-hacks/__tests__/index.js | 12 +- {src => lib}/rules/no-browser-hacks/index.js | 32 +- .../rules/no-descending-specificity/README.md | 0 .../__tests__/index.js | 12 +- .../rules/no-descending-specificity/index.js | 61 +- .../rules/no-duplicate-selectors/README.md | 0 .../__tests__/fixtures/also-using-foo.css | 0 .../__tests__/fixtures/using-foo-twice.css | 0 .../__tests__/fixtures/using-foo.css | 0 .../no-duplicate-selectors/__tests__/index.js | 48 +- .../rules/no-duplicate-selectors/index.js | 49 +- {src => lib}/rules/no-empty-source/README.md | 0 .../rules/no-empty-source/__tests__/index.js | 12 +- lib/rules/no-empty-source/index.js | 35 + .../rules/no-eol-whitespace/README.md | 0 .../no-eol-whitespace/__tests__/index.js | 12 +- {src => lib}/rules/no-eol-whitespace/index.js | 45 +- .../rules/no-extra-semicolons/README.md | 0 .../no-extra-semicolons/__tests__/index.js | 12 +- .../rules/no-extra-semicolons/index.js | 59 +- .../no-indistinguishable-colors/README.md | 0 .../__tests__/index.js | 12 +- .../no-indistinguishable-colors/index.js | 39 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../no-invalid-double-slash-comments/index.js | 24 +- .../README.md | 0 .../__tests__/index.js | 12 +- .../no-missing-end-of-source-newline/index.js | 37 + .../rules/no-unknown-animations/README.md | 0 .../no-unknown-animations/__tests__/index.js | 12 +- lib/rules/no-unknown-animations/index.js | 59 + .../no-unsupported-browser-features/README.md | 0 .../__tests__/index.js | 12 +- .../no-unsupported-browser-features/index.js | 32 +- .../rules/number-leading-zero/README.md | 0 .../number-leading-zero/__tests__/index.js | 12 +- .../rules/number-leading-zero/index.js | 67 +- .../rules/number-max-precision/README.md | 0 .../number-max-precision/__tests__/index.js | 12 +- lib/rules/number-max-precision/index.js | 79 + .../rules/number-no-trailing-zeros/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/number-no-trailing-zeros/index.js | 70 + .../rules/property-blacklist/README.md | 0 .../property-blacklist/__tests__/index.js | 21 +- lib/rules/property-blacklist/index.js | 55 + {src => lib}/rules/property-case/README.md | 0 .../rules/property-case/__tests__/index.js | 12 +- lib/rules/property-case/index.js | 52 + .../rules/property-no-unknown/README.md | 0 .../property-no-unknown/__tests__/index.js | 12 +- lib/rules/property-no-unknown/index.js | 74 + .../rules/property-no-vendor-prefix/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/property-no-vendor-prefix/index.js | 46 + .../rules/property-whitelist/README.md | 0 .../property-whitelist/__tests__/index.js | 21 +- lib/rules/property-whitelist/index.js | 55 + .../root-no-standard-properties/README.md | 0 .../__tests__/index.js | 12 +- .../root-no-standard-properties/index.js | 73 + .../rule-nested-empty-line-before/README.md | 0 .../__tests__/index.js | 12 +- .../rule-nested-empty-line-before/index.js | 49 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 54 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 43 +- .../README.md | 0 .../__tests__/index.js | 17 +- .../index.js | 60 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 38 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 38 + .../README.md | 0 .../__tests__/index.js | 17 +- .../index.js | 60 + .../rules/selector-attribute-quotes/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/selector-attribute-quotes/index.js | 67 + .../rules/selector-class-pattern/README.md | 0 .../selector-class-pattern/__tests__/index.js | 20 +- .../rules/selector-class-pattern/index.js | 78 +- .../selector-combinator-space-after/README.md | 0 .../__tests__/index.js | 14 +- .../selector-combinator-space-after/index.js | 37 + .../README.md | 0 .../__tests__/index.js | 14 +- .../selector-combinator-space-before/index.js | 37 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 56 + .../rules/selector-id-pattern/README.md | 0 .../selector-id-pattern/__tests__/index.js | 18 +- lib/rules/selector-id-pattern/index.js | 67 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 71 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 38 + .../selector-list-comma-space-after/README.md | 0 .../__tests__/index.js | 12 +- .../selector-list-comma-space-after/index.js | 39 + .../README.md | 0 .../__tests__/index.js | 12 +- .../selector-list-comma-space-before/index.js | 32 +- .../selector-max-compound-selectors/README.md | 0 .../__tests__/index.js | 12 +- .../selector-max-compound-selectors/index.js | 50 +- .../rules/selector-max-empty-lines/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/selector-max-empty-lines/index.js | 55 + .../rules/selector-max-specificity/README.md | 0 .../__tests__/index.js | 16 +- .../rules/selector-max-specificity/index.js | 52 +- .../rules/selector-nested-pattern/README.md | 0 .../__tests__/index.js | 18 +- lib/rules/selector-nested-pattern/index.js | 58 + .../rules/selector-no-attribute/README.md | 0 .../selector-no-attribute/__tests__/index.js | 12 +- lib/rules/selector-no-attribute/index.js | 49 + .../rules/selector-no-combinator/README.md | 0 .../selector-no-combinator/__tests__/index.js | 12 +- lib/rules/selector-no-combinator/index.js | 49 + .../rules/selector-no-empty/README.md | 0 .../selector-no-empty/__tests__/index.js | 12 +- lib/rules/selector-no-empty/index.js | 44 + {src => lib}/rules/selector-no-id/README.md | 0 .../rules/selector-no-id/__tests__/index.js | 15 +- lib/rules/selector-no-id/index.js | 57 + .../selector-no-qualifying-type/README.md | 0 .../__tests__/index.js | 12 +- .../selector-no-qualifying-type/index.js | 77 +- {src => lib}/rules/selector-no-type/README.md | 0 .../rules/selector-no-type/__tests__/index.js | 20 +- lib/rules/selector-no-type/index.js | 117 + .../rules/selector-no-universal/README.md | 0 .../selector-no-universal/__tests__/index.js | 12 +- lib/rules/selector-no-universal/index.js | 49 + .../rules/selector-no-vendor-prefix/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/selector-no-vendor-prefix/index.js | 52 + .../selector-pseudo-class-blacklist/README.md | 0 .../__tests__/index.js | 24 +- .../selector-pseudo-class-blacklist/index.js | 71 + .../selector-pseudo-class-case/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/selector-pseudo-class-case/index.js | 71 + .../README.md | 0 .../__tests__/index.js | 12 +- .../selector-pseudo-class-no-unknown/index.js | 83 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 49 +- .../selector-pseudo-class-whitelist/README.md | 0 .../__tests__/index.js | 23 +- .../selector-pseudo-class-whitelist/index.js | 71 + .../selector-pseudo-element-case/README.md | 0 .../__tests__/index.js | 13 +- .../selector-pseudo-element-case/index.js | 71 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 64 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 83 + .../selector-root-no-composition/README.md | 0 .../__tests__/index.js | 12 +- .../selector-root-no-composition/index.js | 37 + .../rules/selector-type-case/README.md | 0 .../selector-type-case/__tests__/index.js | 12 +- lib/rules/selector-type-case/index.js | 72 + .../rules/selector-type-no-unknown/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/selector-type-no-unknown/index.js | 84 + .../selectorAttributeOperatorSpaceChecker.js | 48 + lib/rules/selectorCombinatorSpaceChecker.js | 48 + .../selectorListCommaWhitespaceChecker.js | 32 + .../README.md | 0 .../__tests__/index.js | 12 +- .../index.js | 122 + .../rules/string-no-newline/README.md | 0 .../string-no-newline/__tests__/index.js | 12 +- {src => lib}/rules/string-no-newline/index.js | 30 +- {src => lib}/rules/string-quotes/README.md | 0 .../rules/string-quotes/__tests__/index.js | 12 +- lib/rules/string-quotes/index.js | 41 + .../rules/stylelint-disable-reason/README.md | 0 .../__tests__/index.js | 12 +- .../rules/stylelint-disable-reason/index.js | 50 +- .../rules/time-no-imperceptible/README.md | 0 .../time-no-imperceptible/__tests__/index.js | 12 +- .../rules/time-no-imperceptible/index.js | 55 +- {src => lib}/rules/unit-blacklist/README.md | 0 .../rules/unit-blacklist/__tests__/index.js | 30 +- lib/rules/unit-blacklist/index.js | 73 + {src => lib}/rules/unit-case/README.md | 0 .../rules/unit-case/__tests__/index.js | 12 +- lib/rules/unit-case/index.js | 63 + {src => lib}/rules/unit-no-unknown/README.md | 0 .../rules/unit-no-unknown/__tests__/index.js | 12 +- lib/rules/unit-no-unknown/index.js | 71 + {src => lib}/rules/unit-whitelist/README.md | 0 .../rules/unit-whitelist/__tests__/index.js | 30 +- lib/rules/unit-whitelist/index.js | 73 + .../rules/value-keyword-case/README.md | 0 .../value-keyword-case/__tests__/index.js | 12 +- lib/rules/value-keyword-case/index.js | 135 + .../value-list-comma-newline-after/README.md | 0 .../__tests__/index.js | 12 +- .../value-list-comma-newline-after/index.js | 38 + .../value-list-comma-newline-before/README.md | 0 .../__tests__/index.js | 12 +- .../value-list-comma-newline-before/index.js | 38 + .../value-list-comma-space-after/README.md | 0 .../__tests__/index.js | 12 +- .../value-list-comma-space-after/index.js | 39 + .../value-list-comma-space-before/README.md | 0 .../__tests__/index.js | 12 +- .../value-list-comma-space-before/index.js | 33 +- .../value-list-max-empty-lines/README.md | 0 .../__tests__/index.js | 12 +- lib/rules/value-list-max-empty-lines/index.js | 55 + .../rules/value-no-vendor-prefix/README.md | 0 .../value-no-vendor-prefix/__tests__/index.js | 12 +- lib/rules/value-no-vendor-prefix/index.js | 56 + lib/rules/valueListCommaWhitespaceChecker.js | 37 + lib/standalone.js | 127 + .../__tests__/mergeTestDescriptions-test.js | 6 +- lib/testUtils/basicChecks.js | 16 + {src => lib}/testUtils/createRuleTester.js | 48 +- lib/testUtils/mergeTestDescriptions.js | 17 + lib/testUtils/testRule.js | 20 + .../utils/__tests__/atRuleParamIndex-test.js | 8 +- .../utils/__tests__/beforeBlockString-test.js | 26 +- .../utils/__tests__/blockString-test.js | 12 +- .../utils/__tests__/blurComments-test.js | 6 +- .../__tests__/blurFunctionArguments-test.js | 6 +- .../utils/__tests__/blurInterpolation-test.js | 11 +- lib/utils/__tests__/containsString-test.js | 30 + .../__tests__/declarationValueIndex-test.js | 8 +- lib/utils/__tests__/findAnimationName-test.js | 71 + .../utils/__tests__/findAtRuleContext-test.js | 8 +- lib/utils/__tests__/findFontFamily-test.js | 215 ++ lib/utils/__tests__/findListStyleType-test.js | 97 + {src => lib}/utils/__tests__/fixtures/one.css | 0 {src => lib}/utils/__tests__/fixtures/two.css | 0 .../__tests__/functionArgumentsSearch-test.js | 6 +- .../__tests__/getUnitFromValueNode-test.js | 8 +- {src => lib}/utils/__tests__/hasBlock-test.js | 8 +- .../utils/__tests__/hasEmptyBlock-test.js | 8 +- .../utils/__tests__/hasEmptyLine-test.js | 6 +- .../utils/__tests__/hasInterpolation-test.js | 6 +- ...isCounterIncrementCustomIdentValue-test.js | 6 +- .../__tests__/isCustomMediaQuery-test.js | 6 +- .../utils/__tests__/isCustomProperty-test.js | 6 +- .../__tests__/isCustomPropertySet-test.js | 13 +- .../utils/__tests__/isKeyframeRule-test.js | 8 +- .../__tests__/isKeyframeSelector-test.js | 6 +- .../utils/__tests__/isNumbery-test.js | 6 +- .../utils/__tests__/isOnlyWhitespace-test.js | 6 +- .../isRangeContextMediaFeature-test.js | 6 +- .../__tests__/isSingleLineString-test.js | 10 +- .../__tests__/isStandardSyntaxAtRule-test.js | 16 +- .../isStandardSyntaxDeclaration-test.js | 12 +- .../isStandardSyntaxFunction-test.js | 14 +- .../isStandardSyntaxMediaFeature-test.js | 6 +- .../isStandardSyntaxMediaFeatureName-test.js | 6 +- .../isStandardSyntaxProperty-test.js | 6 +- .../__tests__/isStandardSyntaxRule-test.js | 10 +- .../isStandardSyntaxSelector-test.js | 6 +- .../isStandardSyntaxTypeSelector-test.js | 10 +- .../__tests__/isStandardSyntaxUrl-test.js | 43 +- .../__tests__/isStandardSyntaxValue-test.js | 6 +- .../utils/__tests__/isValidFontSize-test.js | 6 +- .../utils/__tests__/isValidHex-test.js | 6 +- .../utils/__tests__/isVariable-test.js | 6 +- .../__tests__/matchesStringOrRegExp-test.js | 45 +- .../__tests__/nextNonCommentNode-test.js | 8 +- lib/utils/__tests__/nodeContextLookup-test.js | 31 + .../utils/__tests__/optionsMatches-test.js | 6 +- {src => lib}/utils/__tests__/report-test.js | 18 +- .../utils/__tests__/ruleMessages-test.js | 22 +- ...validateObjectWithStringArrayProps-test.js | 12 +- .../utils/__tests__/validateOptions-test.js | 35 +- {src => lib}/utils/atRuleParamIndex.js | 9 +- lib/utils/beforeBlockString.js | 38 + {src => lib}/utils/blockString.js | 15 +- lib/utils/blurComments.js | 7 + {src => lib}/utils/blurFunctionArguments.js | 19 +- lib/utils/blurInterpolation.js | 7 + {src => lib}/utils/configurationError.js | 4 +- {src => lib}/utils/containsString.js | 4 +- {src => lib}/utils/declarationValueIndex.js | 4 +- lib/utils/findAnimationName.js | 57 + {src => lib}/utils/findAtRuleContext.js | 11 +- {src => lib}/utils/findFontFamily.js | 70 +- lib/utils/findListStyleType.js | 51 + {src => lib}/utils/functionArgumentsSearch.js | 12 +- lib/utils/getIsFileIgnored.js | 14 + lib/utils/getModulePath.js | 17 + lib/utils/getUnitFromValueNode.js | 38 + {src => lib}/utils/hasBlock.js | 8 +- {src => lib}/utils/hasEmptyBlock.js | 10 +- lib/utils/hasEmptyLine.js | 11 + lib/utils/hasInterpolation.js | 19 + {src => lib}/utils/hasLessInterpolation.js | 8 +- {src => lib}/utils/hasPsvInterpolation.js | 8 +- {src => lib}/utils/hasScssInterpolation.js | 8 +- {src => lib}/utils/isAutoprefixable.js | 18 +- .../isCounterIncrementCustomIdentValue.js | 21 + {src => lib}/utils/isCustomMediaQuery.js | 6 +- {src => lib}/utils/isCustomProperty.js | 6 +- lib/utils/isCustomPropertySet.js | 16 + lib/utils/isKeyframeRule.js | 13 + lib/utils/isKeyframeSelector.js | 22 + {src => lib}/utils/isNumbery.js | 6 +- {src => lib}/utils/isOnlyWhitespace.js | 6 +- lib/utils/isRangeContextMediaFeature.js | 11 + {src => lib}/utils/isSingleLineString.js | 4 +- {src => lib}/utils/isStandardSyntaxAtRule.js | 12 +- .../utils/isStandardSyntaxDeclaration.js | 24 +- .../utils/isStandardSyntaxFunction.js | 8 +- .../utils/isStandardSyntaxMediaFeature.js | 14 +- .../utils/isStandardSyntaxMediaFeatureName.js | 8 +- .../utils/isStandardSyntaxProperty.js | 18 +- {src => lib}/utils/isStandardSyntaxRule.js | 32 +- lib/utils/isStandardSyntaxSelector.js | 22 + .../utils/isStandardSyntaxTypeSelector.js | 27 +- lib/utils/isStandardSyntaxUrl.js | 47 + lib/utils/isStandardSyntaxValue.js | 27 + lib/utils/isValidFontSize.js | 36 + {src => lib}/utils/isValidHex.js | 7 +- {src => lib}/utils/isVariable.js | 6 +- {src => lib}/utils/isWhitespace.js | 4 +- {src => lib}/utils/matchesStringOrRegExp.js | 15 +- {src => lib}/utils/nextNonCommentNode.js | 4 +- {src => lib}/utils/nodeContextLookup.js | 8 +- {src => lib}/utils/optionsMatches.js | 13 +- lib/utils/parseSelector.js | 11 + {src => lib}/utils/rawNodeString.js | 4 +- {src => lib}/utils/report.js | 51 +- {src => lib}/utils/ruleMessages.js | 14 +- .../validateObjectWithStringArrayProps.js | 16 +- {src => lib}/utils/validateOptions.js | 65 +- {src => lib}/utils/whitespaceChecker.js | 147 +- package.json | 41 +- scripts/benchmark-rule.js | 14 +- scripts/rules-tape.js | 126 + scripts/visual.sh | 4 +- src/__tests__/fixtures/plugin-array.js | 7 - src/__tests__/message.test.js | 12 - src/__tests__/needlessDisables.test.js | 110 - src/createPlugin.js | 6 - .../needlessDisablesStringFormatter-test.js | 40 - src/formatters/index.js | 3 - src/getConfigForFile.js | 47 - src/getPostcssResult.js | 98 - src/index.js | 27 - src/postcssPlugin.js | 18 - src/reference/keywordSets.js | 598 ---- src/reference/namedColorData.js | 3030 ----------------- src/reference/propertySets.js | 12 - src/reference/punctuationSets.js | 14 - src/reference/shorthandData.js | 120 - src/rules/at-rule-blacklist/index.js | 41 - src/rules/at-rule-empty-line-before/index.js | 132 - src/rules/at-rule-name-case/index.js | 38 - src/rules/at-rule-name-newline-after/index.js | 33 - src/rules/at-rule-name-space-after/index.js | 61 - src/rules/at-rule-no-unknown/index.js | 45 - src/rules/at-rule-no-vendor-prefix/index.js | 37 - .../at-rule-semicolon-newline-after/index.js | 51 - src/rules/at-rule-whitelist/index.js | 41 - .../index.js | 69 - src/rules/block-no-single-line/index.js | 40 - src/rules/color-named/index.js | 130 - src/rules/comment-empty-line-before/index.js | 87 - src/rules/comment-no-empty/index.js | 31 - src/rules/comment-word-blacklist/index.js | 48 - src/rules/custom-media-pattern/index.js | 46 - .../index.js | 105 - .../custom-property-no-outside-root/index.js | 34 - src/rules/custom-property-pattern/index.js | 43 - .../declaration-bang-space-after/index.js | 61 - .../declaration-bang-space-before/index.js | 34 - .../index.js | 74 - .../index.js | 48 - .../index.js | 42 - .../index.js | 59 - .../declaration-colon-space-after/index.js | 70 - .../declaration-colon-space-before/index.js | 34 - .../declaration-empty-line-before/index.js | 117 - src/rules/declaration-no-important/index.js | 30 - .../index.js | 57 - .../index.js | 57 - .../index.js | 45 - .../index.js | 45 - src/rules/function-blacklist/index.js | 47 - .../function-comma-newline-after/index.js | 36 - src/rules/function-comma-space-after/index.js | 94 - src/rules/function-max-empty-lines/index.js | 59 - src/rules/function-url-data-uris/index.js | 60 - .../function-url-no-scheme-relative/index.js | 37 - .../function-url-scheme-whitelist/index.js | 64 - src/rules/function-whitelist/index.js | 47 - src/rules/indentation/__tests__/at-rules.js | 302 -- src/rules/indentation/__tests__/comments.js | 95 - src/rules/indentation/__tests__/functions.js | 629 ---- src/rules/indentation/__tests__/rules.js | 615 ---- src/rules/indentation/__tests__/selectors.js | 142 - src/rules/index.js | 347 -- .../index.js | 31 - .../media-feature-colon-space-after/index.js | 58 - .../media-feature-colon-space-before/index.js | 35 - .../media-feature-name-blacklist/index.js | 49 - src/rules/media-feature-name-case/index.js | 54 - .../media-feature-name-no-unknown/index.js | 57 - .../index.js | 34 - .../media-feature-name-whitelist/index.js | 49 - .../index.js | 35 - .../index.js | 60 - src/rules/no-empty-source/index.js | 27 - .../no-missing-end-of-source-newline/index.js | 29 - src/rules/no-unknown-animations/index.js | 47 - src/rules/number-max-precision/index.js | 63 - src/rules/number-no-trailing-zeros/index.js | 56 - src/rules/property-blacklist/index.js | 44 - src/rules/property-case/index.js | 42 - src/rules/property-no-unknown/index.js | 56 - src/rules/property-no-vendor-prefix/index.js | 35 - src/rules/property-whitelist/index.js | 44 - .../root-no-standard-properties/index.js | 60 - .../rule-nested-empty-line-before/index.js | 50 - .../index.js | 92 - .../index.js | 52 - .../index.js | 88 - .../index.js | 36 - .../index.js | 52 - src/rules/selector-attribute-quotes/index.js | 66 - .../selector-combinator-space-after/index.js | 73 - .../selector-combinator-space-before/index.js | 35 - .../index.js | 44 - src/rules/selector-id-pattern/index.js | 54 - .../index.js | 68 - .../index.js | 37 - .../selector-list-comma-space-after/index.js | 66 - src/rules/selector-max-empty-lines/index.js | 53 - src/rules/selector-nested-pattern/index.js | 46 - src/rules/selector-no-attribute/index.js | 38 - src/rules/selector-no-combinator/index.js | 38 - src/rules/selector-no-empty/index.js | 37 - src/rules/selector-no-id/index.js | 42 - src/rules/selector-no-type/index.js | 96 - src/rules/selector-no-universal/index.js | 38 - src/rules/selector-no-vendor-prefix/index.js | 41 - .../selector-pseudo-class-blacklist/index.js | 58 - src/rules/selector-pseudo-class-case/index.js | 64 - .../selector-pseudo-class-no-unknown/index.js | 71 - .../selector-pseudo-class-whitelist/index.js | 58 - .../selector-pseudo-element-case/index.js | 64 - .../index.js | 53 - .../index.js | 67 - .../selector-root-no-composition/index.js | 32 - src/rules/selector-type-case/index.js | 56 - src/rules/selector-type-no-unknown/index.js | 91 - .../index.js | 119 - src/rules/string-quotes/index.js | 38 - src/rules/unit-blacklist/index.js | 67 - src/rules/unit-case/index.js | 58 - src/rules/unit-no-unknown/index.js | 61 - src/rules/unit-whitelist/index.js | 67 - src/rules/value-keyword-case/index.js | 138 - .../value-list-comma-newline-after/index.js | 36 - .../value-list-comma-newline-before/index.js | 36 - .../value-list-comma-space-after/index.js | 73 - src/rules/value-list-max-empty-lines/index.js | 53 - src/rules/value-no-vendor-prefix/index.js | 48 - src/standalone.js | 138 - src/testUtils/basicChecks.js | 19 - src/testUtils/index.js | 2 - src/testUtils/mergeTestDescriptions.js | 11 - src/testUtils/testRule.js | 18 - src/utils/__tests__/containsString-test.js | 35 - src/utils/__tests__/findAnimationName-test.js | 111 - src/utils/__tests__/findFontFamily-test.js | 331 -- src/utils/__tests__/findListStyleType-test.js | 155 - src/utils/__tests__/nodeContextLookup-test.js | 56 - src/utils/beforeBlockString.js | 36 - src/utils/blurComments.js | 14 - src/utils/blurInterpolation.js | 16 - src/utils/findAnimationName.js | 48 - src/utils/findListStyleType.js | 47 - src/utils/getIsFileIgnored.js | 15 - src/utils/getModulePath.js | 20 - src/utils/getUnitFromValueNode.js | 35 - src/utils/hasEmptyLine.js | 13 - src/utils/hasInterpolation.js | 17 - src/utils/index.js | 59 - .../isCounterIncrementCustomIdentValue.js | 17 - src/utils/isCustomPropertySet.js | 14 - src/utils/isKeyframeRule.js | 13 - src/utils/isKeyframeSelector.js | 16 - src/utils/isRangeContextMediaFeature.js | 13 - src/utils/isStandardSyntaxSelector.js | 16 - src/utils/isStandardSyntaxUrl.js | 42 - src/utils/isStandardSyntaxValue.js | 19 - src/utils/isValidFontSize.js | 26 - src/utils/parseSelector.js | 9 - system-tests/001/001.test.js | 5 +- system-tests/002/002.test.js | 5 +- system-tests/003/003.test.js | 5 +- system-tests/systemTestUtils.js | 26 +- 941 files changed, 15410 insertions(+), 18369 deletions(-) create mode 100755 bin/stylelint rename {src => lib}/__tests__/createLinter.test.js (58%) rename {src => lib}/__tests__/defaultSeverity.test.js (86%) rename {src => lib}/__tests__/disableRanges-integration-test.js (70%) rename {src => lib}/__tests__/disableRanges.test.js (61%) rename {src => lib}/__tests__/extends.test.js (55%) rename {src => lib}/__tests__/fixtures/config-at-rule-empty-line-before.json (100%) rename {src => lib}/__tests__/fixtures/config-block-empty-ok.json (100%) rename {src => lib}/__tests__/fixtures/config-block-no-empty.json (100%) rename {src => lib}/__tests__/fixtures/config-color-named-custom-message.yaml (100%) rename {src => lib}/__tests__/fixtures/config-color-no-invalid-hex.json (100%) rename {src => lib}/__tests__/fixtures/config-extending-and-ignoring.json (100%) rename {src => lib}/__tests__/fixtures/config-extending-another-extend.json (100%) rename {src => lib}/__tests__/fixtures/config-extending-one.json (100%) rename {src => lib}/__tests__/fixtures/config-extending-three-with-override.json (100%) rename {src => lib}/__tests__/fixtures/config-extending-two.json (100%) rename {src => lib}/__tests__/fixtures/config-extending-with-plugin.json (100%) rename {src => lib}/__tests__/fixtures/config-no-pixels.json (100%) rename {src => lib}/__tests__/fixtures/config-per-file/a/.stylelintrc (100%) rename {src => lib}/__tests__/fixtures/config-per-file/a/a.css (100%) rename {src => lib}/__tests__/fixtures/config-per-file/a/b/.stylelintrc (100%) rename {src => lib}/__tests__/fixtures/config-per-file/a/b/b.css (100%) rename {src => lib}/__tests__/fixtures/config-per-file/a/b/c/.stylelintrc (100%) rename {src => lib}/__tests__/fixtures/config-per-file/a/b/c/c.css (100%) rename {src => lib}/__tests__/fixtures/config-per-file/a/b/c/d/d.css (100%) rename {src => lib}/__tests__/fixtures/config-plugin-extending-with-plugin.json (100%) rename {src => lib}/__tests__/fixtures/config-relative-plugin-nested.json (100%) rename {src => lib}/__tests__/fixtures/config-relative-plugin.json (100%) rename {src => lib}/__tests__/fixtures/config-string-quotes-single.json (100%) rename {src => lib}/__tests__/fixtures/config-with-undefined-rule.json (100%) rename {src => lib}/__tests__/fixtures/config-without-rules.json (100%) rename {src => lib}/__tests__/fixtures/custom-parser.js (81%) rename {src => lib}/__tests__/fixtures/custom-syntax.js (80%) rename {src => lib}/__tests__/fixtures/empty-block-with-disables.css (100%) rename {src => lib}/__tests__/fixtures/empty-block.css (100%) rename {src => lib}/__tests__/fixtures/extension-sensitive.less (100%) rename {src => lib}/__tests__/fixtures/extension-sensitive.scss (100%) rename {src => lib}/__tests__/fixtures/extension-sensitive.sss (100%) rename {src => lib}/__tests__/fixtures/getConfigForFile/a/.stylelintrc (100%) rename {src => lib}/__tests__/fixtures/getConfigForFile/a/b/foo.css (100%) rename {src => lib}/__tests__/fixtures/ignore.txt (100%) rename {src => lib}/__tests__/fixtures/invalid-hex.css (100%) rename {src => lib}/__tests__/fixtures/invalid-hex.html (100%) rename {src => lib}/__tests__/fixtures/needlessDisables/.stylelintignore (100%) rename {src => lib}/__tests__/fixtures/needlessDisables/disabled-ranges-1.css (100%) rename {src => lib}/__tests__/fixtures/needlessDisables/disabled-ranges-2.css (100%) rename {src => lib}/__tests__/fixtures/needlessDisables/disabled-ranges-3.css (100%) rename {src => lib}/__tests__/fixtures/needlessDisables/ignored-file.css (100%) rename {src => lib}/__tests__/fixtures/nested-plugin/plugin-nested-warn-about-foo.js (79%) create mode 100644 lib/__tests__/fixtures/plugin-array.js rename {src => lib}/__tests__/fixtures/plugin-conditionally-check-color-hex-case.js (69%) rename {src => lib}/__tests__/fixtures/plugin-primary-array.js (74%) rename {src => lib}/__tests__/fixtures/plugin-slashless-warn-about-foo.js (79%) rename {src => lib}/__tests__/fixtures/plugin-warn-about-bar.js (79%) rename {src => lib}/__tests__/fixtures/plugin-warn-about-foo.js (79%) rename {src => lib}/__tests__/fixtures/processor-fenced-blocks.js (55%) rename {src => lib}/__tests__/fixtures/processor-triple-question-marks.js (70%) rename {src => lib}/__tests__/fixtures/standaloneNoParsing/no-syntax-error.css (100%) rename {src => lib}/__tests__/fixtures/standaloneNoParsing/syntax-error-ignored.scss (100%) rename {src => lib}/__tests__/ignore.test.js (89%) rename {src => lib}/__tests__/ignoreDisables.test.js (88%) rename {src => lib}/__tests__/integration.test.js (79%) create mode 100644 lib/__tests__/message.test.js create mode 100644 lib/__tests__/needlessDisables.test.js rename {src => lib}/__tests__/normalizeRuleSettings-integration.test.js (71%) rename {src => lib}/__tests__/normalizeRuleSettings.test.js (98%) rename {src => lib}/__tests__/plugins.test.js (86%) rename {src => lib}/__tests__/postcssPlugin.test.js (81%) rename {src => lib}/__tests__/processors.test.js (77%) rename {src => lib}/__tests__/standalone-formatter.test.js (65%) rename {src => lib}/__tests__/standalone-needlessDisables.test.js (83%) rename {src => lib}/__tests__/standalone-quiet.test.js (51%) rename {src => lib}/__tests__/standalone-syntax.test.js (83%) rename {src => lib}/__tests__/standalone.test.js (84%) rename {src => lib}/__tests__/stylelintignore-test/.stylelintignore (100%) rename {src => lib}/__tests__/stylelintignore-test/stylelintignore.test.js (88%) rename {src => lib}/assignDisabledRanges.js (79%) rename {src => lib}/augmentConfig.js (51%) rename {src => lib}/cli.js (80%) create mode 100644 lib/createPlugin.js rename {src => lib}/createStylelint.js (54%) rename {src => lib}/createStylelintResult.js (61%) rename {src => lib}/formatters/__tests__/jsonFormatter-test.js (62%) create mode 100644 lib/formatters/__tests__/needlessDisablesStringFormatter-test.js create mode 100644 lib/formatters/__tests__/prepareFormatterOutput.js rename {src => lib}/formatters/__tests__/stringFormatter-test.js (76%) rename {src => lib}/formatters/__tests__/verboseFormatter-test.js (82%) create mode 100644 lib/formatters/index.js rename {src => lib}/formatters/jsonFormatter.js (73%) rename {src => lib}/formatters/needlessDisablesStringFormatter.js (81%) rename {src => lib}/formatters/stringFormatter.js (60%) rename {src => lib}/formatters/verboseFormatter.js (67%) create mode 100644 lib/getConfigForFile.js create mode 100644 lib/getPostcssResult.js create mode 100644 lib/index.js rename {src => lib}/isPathIgnored.js (59%) rename {src => lib}/lintSource.js (64%) rename {src => lib}/needlessDisables.js (65%) rename {src => lib}/normalizeRuleSettings.js (55%) create mode 100644 lib/postcssPlugin.js create mode 100644 lib/reference/keywordSets.js create mode 100644 lib/reference/namedColorData.js create mode 100644 lib/reference/propertySets.js create mode 100644 lib/reference/punctuationSets.js create mode 100644 lib/reference/shorthandData.js rename {src => lib}/rules/at-rule-blacklist/README.md (100%) rename {src => lib}/rules/at-rule-blacklist/__tests__/index.js (92%) create mode 100644 lib/rules/at-rule-blacklist/index.js rename {src => lib}/rules/at-rule-empty-line-before/README.md (100%) rename {src => lib}/rules/at-rule-empty-line-before/__tests__/index.js (97%) create mode 100644 lib/rules/at-rule-empty-line-before/index.js rename {src => lib}/rules/at-rule-name-case/README.md (100%) rename {src => lib}/rules/at-rule-name-case/__tests__/index.js (96%) create mode 100644 lib/rules/at-rule-name-case/index.js rename {src => lib}/rules/at-rule-name-newline-after/README.md (100%) rename {src => lib}/rules/at-rule-name-newline-after/__tests__/index.js (97%) create mode 100644 lib/rules/at-rule-name-newline-after/index.js rename {src => lib}/rules/at-rule-name-space-after/README.md (100%) rename {src => lib}/rules/at-rule-name-space-after/__tests__/index.js (97%) create mode 100644 lib/rules/at-rule-name-space-after/index.js rename {src => lib}/rules/at-rule-no-unknown/README.md (100%) rename {src => lib}/rules/at-rule-no-unknown/__tests__/index.js (93%) create mode 100644 lib/rules/at-rule-no-unknown/index.js rename {src => lib}/rules/at-rule-no-vendor-prefix/README.md (100%) rename {src => lib}/rules/at-rule-no-vendor-prefix/__tests__/index.js (80%) create mode 100644 lib/rules/at-rule-no-vendor-prefix/index.js rename {src => lib}/rules/at-rule-semicolon-newline-after/README.md (100%) rename {src => lib}/rules/at-rule-semicolon-newline-after/__tests__/index.js (91%) create mode 100644 lib/rules/at-rule-semicolon-newline-after/index.js rename {src => lib}/rules/at-rule-whitelist/README.md (100%) rename {src => lib}/rules/at-rule-whitelist/__tests__/index.js (92%) create mode 100644 lib/rules/at-rule-whitelist/index.js create mode 100644 lib/rules/atRuleNameSpaceChecker.js rename {src => lib}/rules/block-closing-brace-empty-line-before/README.md (100%) rename {src => lib}/rules/block-closing-brace-empty-line-before/__tests__/index.js (94%) create mode 100644 lib/rules/block-closing-brace-empty-line-before/index.js rename {src => lib}/rules/block-closing-brace-newline-after/README.md (100%) rename {src => lib}/rules/block-closing-brace-newline-after/__tests__/index.js (98%) rename {src => lib}/rules/block-closing-brace-newline-after/index.js (56%) rename {src => lib}/rules/block-closing-brace-newline-before/README.md (100%) rename {src => lib}/rules/block-closing-brace-newline-before/__tests__/index.js (97%) rename {src => lib}/rules/block-closing-brace-newline-before/index.js (61%) rename {src => lib}/rules/block-closing-brace-space-after/README.md (100%) rename {src => lib}/rules/block-closing-brace-space-after/__tests__/index.js (98%) rename {src => lib}/rules/block-closing-brace-space-after/index.js (62%) rename {src => lib}/rules/block-closing-brace-space-before/README.md (100%) rename {src => lib}/rules/block-closing-brace-space-before/__tests__/index.js (97%) rename {src => lib}/rules/block-closing-brace-space-before/index.js (58%) rename {src => lib}/rules/block-no-empty/README.md (100%) rename {src => lib}/rules/block-no-empty/__tests__/index.js (90%) rename {src => lib}/rules/block-no-empty/index.js (51%) rename {src => lib}/rules/block-no-single-line/README.md (100%) rename {src => lib}/rules/block-no-single-line/__tests__/index.js (92%) create mode 100644 lib/rules/block-no-single-line/index.js rename {src => lib}/rules/block-opening-brace-newline-after/README.md (100%) rename {src => lib}/rules/block-opening-brace-newline-after/__tests__/index.js (96%) rename {src => lib}/rules/block-opening-brace-newline-after/index.js (51%) rename {src => lib}/rules/block-opening-brace-newline-before/README.md (100%) rename {src => lib}/rules/block-opening-brace-newline-before/__tests__/index.js (98%) rename {src => lib}/rules/block-opening-brace-newline-before/index.js (57%) rename {src => lib}/rules/block-opening-brace-space-after/README.md (100%) rename {src => lib}/rules/block-opening-brace-space-after/__tests__/index.js (97%) rename {src => lib}/rules/block-opening-brace-space-after/index.js (56%) rename {src => lib}/rules/block-opening-brace-space-before/README.md (100%) rename {src => lib}/rules/block-opening-brace-space-before/__tests__/index.js (97%) rename {src => lib}/rules/block-opening-brace-space-before/index.js (57%) create mode 100644 lib/rules/checkRuleEmptyLineBefore.js rename {src => lib}/rules/color-hex-case/README.md (100%) rename {src => lib}/rules/color-hex-case/__tests__/index.js (90%) rename {src => lib}/rules/color-hex-case/index.js (58%) rename {src => lib}/rules/color-hex-length/README.md (100%) rename {src => lib}/rules/color-hex-length/__tests__/index.js (91%) rename {src => lib}/rules/color-hex-length/index.js (61%) rename {src => lib}/rules/color-named/README.md (100%) rename {src => lib}/rules/color-named/__tests__/index.js (96%) create mode 100644 lib/rules/color-named/index.js rename {src => lib}/rules/color-no-hex/README.md (100%) rename {src => lib}/rules/color-no-hex/__tests__/index.js (74%) rename {src => lib}/rules/color-no-hex/index.js (58%) rename {src => lib}/rules/color-no-invalid-hex/README.md (100%) rename {src => lib}/rules/color-no-invalid-hex/__tests__/index.js (69%) rename {src => lib}/rules/color-no-invalid-hex/index.js (53%) rename {src => lib}/rules/comment-empty-line-before/README.md (100%) rename {src => lib}/rules/comment-empty-line-before/__tests__/index.js (94%) create mode 100644 lib/rules/comment-empty-line-before/index.js rename {src => lib}/rules/comment-no-empty/README.md (100%) rename {src => lib}/rules/comment-no-empty/__tests__/index.js (84%) create mode 100644 lib/rules/comment-no-empty/index.js rename {src => lib}/rules/comment-whitespace-inside/README.md (100%) rename {src => lib}/rules/comment-whitespace-inside/__tests__/index.js (95%) rename {src => lib}/rules/comment-whitespace-inside/index.js (70%) rename {src => lib}/rules/comment-word-blacklist/README.md (100%) rename {src => lib}/rules/comment-word-blacklist/__tests__/index.js (92%) create mode 100644 lib/rules/comment-word-blacklist/index.js rename {src => lib}/rules/custom-media-pattern/README.md (100%) rename {src => lib}/rules/custom-media-pattern/__tests__/index.js (90%) create mode 100644 lib/rules/custom-media-pattern/index.js rename {src => lib}/rules/custom-property-empty-line-before/README.md (100%) rename {src => lib}/rules/custom-property-empty-line-before/__tests__/index.js (95%) create mode 100644 lib/rules/custom-property-empty-line-before/index.js rename {src => lib}/rules/custom-property-no-outside-root/README.md (100%) rename {src => lib}/rules/custom-property-no-outside-root/__tests__/index.js (81%) create mode 100644 lib/rules/custom-property-no-outside-root/index.js rename {src => lib}/rules/custom-property-pattern/README.md (100%) rename {src => lib}/rules/custom-property-pattern/__tests__/index.js (85%) create mode 100644 lib/rules/custom-property-pattern/index.js rename {src => lib}/rules/declaration-bang-space-after/README.md (100%) rename {src => lib}/rules/declaration-bang-space-after/__tests__/index.js (92%) create mode 100644 lib/rules/declaration-bang-space-after/index.js rename {src => lib}/rules/declaration-bang-space-before/README.md (100%) rename {src => lib}/rules/declaration-bang-space-before/__tests__/index.js (92%) create mode 100644 lib/rules/declaration-bang-space-before/index.js rename {src => lib}/rules/declaration-block-no-duplicate-properties/README.md (100%) rename {src => lib}/rules/declaration-block-no-duplicate-properties/__tests__/index.js (96%) rename {src => lib}/rules/declaration-block-no-duplicate-properties/index.js (63%) rename {src => lib}/rules/declaration-block-no-ignored-properties/README.md (100%) rename {src => lib}/rules/declaration-block-no-ignored-properties/__tests__/index.js (98%) rename {src => lib}/rules/declaration-block-no-ignored-properties/index.js (50%) rename {src => lib}/rules/declaration-block-no-redundant-longhand-properties/README.md (100%) rename {src => lib}/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js (92%) create mode 100644 lib/rules/declaration-block-no-redundant-longhand-properties/index.js rename {src => lib}/rules/declaration-block-no-shorthand-property-overrides/README.md (100%) rename {src => lib}/rules/declaration-block-no-shorthand-property-overrides/__tests__/index.js (91%) create mode 100644 lib/rules/declaration-block-no-shorthand-property-overrides/index.js rename {src => lib}/rules/declaration-block-properties-order/README.md (100%) rename {src => lib}/rules/declaration-block-properties-order/__tests__/alphabetical.js (93%) rename {src => lib}/rules/declaration-block-properties-order/__tests__/flat.js (89%) rename {src => lib}/rules/declaration-block-properties-order/__tests__/grouped-flexible.js (82%) rename {src => lib}/rules/declaration-block-properties-order/__tests__/grouped-strict.js (81%) rename {src => lib}/rules/declaration-block-properties-order/__tests__/validate-options.js (70%) rename {src => lib}/rules/declaration-block-properties-order/index.js (68%) rename {src => lib}/rules/declaration-block-semicolon-newline-after/README.md (100%) rename {src => lib}/rules/declaration-block-semicolon-newline-after/__tests__/index.js (96%) rename {src => lib}/rules/declaration-block-semicolon-newline-after/index.js (56%) rename {src => lib}/rules/declaration-block-semicolon-newline-before/README.md (100%) rename {src => lib}/rules/declaration-block-semicolon-newline-before/__tests__/index.js (95%) rename {src => lib}/rules/declaration-block-semicolon-newline-before/index.js (60%) rename {src => lib}/rules/declaration-block-semicolon-space-after/README.md (100%) rename {src => lib}/rules/declaration-block-semicolon-space-after/__tests__/index.js (95%) rename {src => lib}/rules/declaration-block-semicolon-space-after/index.js (59%) rename {src => lib}/rules/declaration-block-semicolon-space-before/README.md (100%) rename {src => lib}/rules/declaration-block-semicolon-space-before/__tests__/index.js (96%) rename {src => lib}/rules/declaration-block-semicolon-space-before/index.js (62%) rename {src => lib}/rules/declaration-block-single-line-max-declarations/README.md (100%) rename {src => lib}/rules/declaration-block-single-line-max-declarations/__tests__/index.js (89%) create mode 100644 lib/rules/declaration-block-single-line-max-declarations/index.js rename {src => lib}/rules/declaration-block-trailing-semicolon/README.md (100%) rename {src => lib}/rules/declaration-block-trailing-semicolon/__tests__/index.js (94%) create mode 100644 lib/rules/declaration-block-trailing-semicolon/index.js rename {src => lib}/rules/declaration-colon-newline-after/README.md (100%) rename {src => lib}/rules/declaration-colon-newline-after/__tests__/index.js (76%) rename {src => lib}/rules/declaration-colon-newline-after/index.js (55%) rename {src => lib}/rules/declaration-colon-space-after/README.md (100%) rename {src => lib}/rules/declaration-colon-space-after/__tests__/index.js (95%) create mode 100644 lib/rules/declaration-colon-space-after/index.js rename {src => lib}/rules/declaration-colon-space-before/README.md (100%) rename {src => lib}/rules/declaration-colon-space-before/__tests__/index.js (93%) create mode 100644 lib/rules/declaration-colon-space-before/index.js rename {src => lib}/rules/declaration-empty-line-before/README.md (100%) rename {src => lib}/rules/declaration-empty-line-before/__tests__/index.js (97%) create mode 100644 lib/rules/declaration-empty-line-before/index.js rename {src => lib}/rules/declaration-no-important/README.md (100%) rename {src => lib}/rules/declaration-no-important/__tests__/index.js (76%) create mode 100644 lib/rules/declaration-no-important/index.js rename {src => lib}/rules/declaration-property-unit-blacklist/README.md (100%) rename {src => lib}/rules/declaration-property-unit-blacklist/__tests__/index.js (94%) create mode 100644 lib/rules/declaration-property-unit-blacklist/index.js rename {src => lib}/rules/declaration-property-unit-whitelist/README.md (100%) rename {src => lib}/rules/declaration-property-unit-whitelist/__tests__/index.js (94%) create mode 100644 lib/rules/declaration-property-unit-whitelist/index.js rename {src => lib}/rules/declaration-property-value-blacklist/README.md (100%) rename {src => lib}/rules/declaration-property-value-blacklist/__tests__/index.js (91%) create mode 100644 lib/rules/declaration-property-value-blacklist/index.js rename {src => lib}/rules/declaration-property-value-whitelist/README.md (100%) rename {src => lib}/rules/declaration-property-value-whitelist/__tests__/index.js (81%) create mode 100644 lib/rules/declaration-property-value-whitelist/index.js create mode 100644 lib/rules/declarationBangSpaceChecker.js create mode 100644 lib/rules/declarationColonSpaceChecker.js create mode 100644 lib/rules/findMediaOperator.js rename {src => lib}/rules/font-family-name-quotes/README.md (100%) rename {src => lib}/rules/font-family-name-quotes/__tests__/index.js (91%) rename {src => lib}/rules/font-family-name-quotes/index.js (65%) rename {src => lib}/rules/font-family-no-duplicate-names/README.md (100%) rename {src => lib}/rules/font-family-no-duplicate-names/__tests__/index.js (88%) rename {src => lib}/rules/font-family-no-duplicate-names/index.js (57%) rename {src => lib}/rules/font-weight-notation/README.md (100%) rename {src => lib}/rules/font-weight-notation/__tests__/index.js (97%) rename {src => lib}/rules/font-weight-notation/index.js (55%) rename {src => lib}/rules/function-blacklist/README.md (100%) rename {src => lib}/rules/function-blacklist/__tests__/index.js (90%) create mode 100644 lib/rules/function-blacklist/index.js rename {src => lib}/rules/function-calc-no-unspaced-operator/README.md (100%) rename {src => lib}/rules/function-calc-no-unspaced-operator/__tests__/index.js (97%) rename {src => lib}/rules/function-calc-no-unspaced-operator/index.js (66%) rename {src => lib}/rules/function-comma-newline-after/README.md (100%) rename {src => lib}/rules/function-comma-newline-after/__tests__/index.js (96%) create mode 100644 lib/rules/function-comma-newline-after/index.js rename {src => lib}/rules/function-comma-newline-before/README.md (100%) rename {src => lib}/rules/function-comma-newline-before/__tests__/index.js (96%) rename {src => lib}/rules/function-comma-newline-before/index.js (50%) rename {src => lib}/rules/function-comma-space-after/README.md (100%) rename {src => lib}/rules/function-comma-space-after/__tests__/index.js (97%) create mode 100644 lib/rules/function-comma-space-after/index.js rename {src => lib}/rules/function-comma-space-before/README.md (100%) rename {src => lib}/rules/function-comma-space-before/__tests__/index.js (97%) rename {src => lib}/rules/function-comma-space-before/index.js (52%) rename {src => lib}/rules/function-linear-gradient-no-nonstandard-direction/README.md (100%) rename {src => lib}/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js (94%) rename {src => lib}/rules/function-linear-gradient-no-nonstandard-direction/index.js (56%) rename {src => lib}/rules/function-max-empty-lines/README.md (100%) rename {src => lib}/rules/function-max-empty-lines/__tests__/index.js (96%) create mode 100644 lib/rules/function-max-empty-lines/index.js rename {src => lib}/rules/function-name-case/README.md (100%) rename {src => lib}/rules/function-name-case/__tests__/index.js (98%) rename {src => lib}/rules/function-name-case/index.js (52%) rename {src => lib}/rules/function-parentheses-newline-inside/README.md (100%) rename {src => lib}/rules/function-parentheses-newline-inside/__tests__/index.js (96%) rename {src => lib}/rules/function-parentheses-newline-inside/index.js (70%) rename {src => lib}/rules/function-parentheses-space-inside/README.md (100%) rename {src => lib}/rules/function-parentheses-space-inside/__tests__/index.js (98%) rename {src => lib}/rules/function-parentheses-space-inside/index.js (72%) rename {src => lib}/rules/function-url-data-uris/README.md (100%) rename {src => lib}/rules/function-url-data-uris/__tests__/index.js (98%) create mode 100644 lib/rules/function-url-data-uris/index.js rename {src => lib}/rules/function-url-no-scheme-relative/README.md (100%) rename {src => lib}/rules/function-url-no-scheme-relative/__tests__/index.js (92%) create mode 100644 lib/rules/function-url-no-scheme-relative/index.js rename {src => lib}/rules/function-url-quotes/README.md (100%) rename {src => lib}/rules/function-url-quotes/__tests__/index.js (98%) rename {src => lib}/rules/function-url-quotes/index.js (70%) rename {src => lib}/rules/function-url-scheme-whitelist/README.md (100%) rename {src => lib}/rules/function-url-scheme-whitelist/__tests__/index.js (96%) create mode 100644 lib/rules/function-url-scheme-whitelist/index.js rename {src => lib}/rules/function-whitelist/README.md (100%) rename {src => lib}/rules/function-whitelist/__tests__/index.js (90%) create mode 100644 lib/rules/function-whitelist/index.js rename {src => lib}/rules/function-whitespace-after/README.md (100%) rename {src => lib}/rules/function-whitespace-after/__tests__/index.js (95%) rename {src => lib}/rules/function-whitespace-after/index.js (60%) create mode 100644 lib/rules/functionCommaSpaceChecker.js rename {src => lib}/rules/indentation/README.md (100%) create mode 100644 lib/rules/indentation/__tests__/at-rules.js create mode 100644 lib/rules/indentation/__tests__/comments.js create mode 100644 lib/rules/indentation/__tests__/functions.js create mode 100644 lib/rules/indentation/__tests__/rules.js create mode 100644 lib/rules/indentation/__tests__/selectors.js rename {src => lib}/rules/indentation/index.js (68%) create mode 100644 lib/rules/index.js rename {src => lib}/rules/keyframe-declaration-no-important/README.md (100%) rename {src => lib}/rules/keyframe-declaration-no-important/__tests__/index.js (90%) create mode 100644 lib/rules/keyframe-declaration-no-important/index.js rename {src => lib}/rules/length-zero-no-unit/README.md (100%) rename {src => lib}/rules/length-zero-no-unit/__tests__/index.js (96%) rename {src => lib}/rules/length-zero-no-unit/index.js (55%) rename {src => lib}/rules/max-empty-lines/README.md (100%) rename {src => lib}/rules/max-empty-lines/__tests__/index.js (92%) rename {src => lib}/rules/max-empty-lines/index.js (59%) rename {src => lib}/rules/max-line-length/README.md (100%) rename {src => lib}/rules/max-line-length/__tests__/index.js (95%) rename {src => lib}/rules/max-line-length/index.js (73%) rename {src => lib}/rules/max-nesting-depth/README.md (100%) rename {src => lib}/rules/max-nesting-depth/__tests__/index.js (91%) rename {src => lib}/rules/max-nesting-depth/index.js (57%) rename {src => lib}/rules/media-feature-colon-space-after/README.md (100%) rename {src => lib}/rules/media-feature-colon-space-after/__tests__/index.js (94%) create mode 100644 lib/rules/media-feature-colon-space-after/index.js rename {src => lib}/rules/media-feature-colon-space-before/README.md (100%) rename {src => lib}/rules/media-feature-colon-space-before/__tests__/index.js (94%) create mode 100644 lib/rules/media-feature-colon-space-before/index.js rename {src => lib}/rules/media-feature-name-blacklist/README.md (100%) rename {src => lib}/rules/media-feature-name-blacklist/__tests__/index.js (88%) create mode 100644 lib/rules/media-feature-name-blacklist/index.js rename {src => lib}/rules/media-feature-name-case/README.md (100%) rename {src => lib}/rules/media-feature-name-case/__tests__/index.js (97%) create mode 100644 lib/rules/media-feature-name-case/index.js rename {src => lib}/rules/media-feature-name-no-unknown/README.md (100%) rename {src => lib}/rules/media-feature-name-no-unknown/__tests__/index.js (94%) create mode 100644 lib/rules/media-feature-name-no-unknown/index.js rename {src => lib}/rules/media-feature-name-no-vendor-prefix/README.md (100%) rename {src => lib}/rules/media-feature-name-no-vendor-prefix/__tests__/index.js (80%) create mode 100644 lib/rules/media-feature-name-no-vendor-prefix/index.js rename {src => lib}/rules/media-feature-name-whitelist/README.md (100%) rename {src => lib}/rules/media-feature-name-whitelist/__tests__/index.js (88%) create mode 100644 lib/rules/media-feature-name-whitelist/index.js rename {src => lib}/rules/media-feature-no-missing-punctuation/README.md (100%) rename {src => lib}/rules/media-feature-no-missing-punctuation/__tests__/index.js (92%) rename {src => lib}/rules/media-feature-no-missing-punctuation/index.js (52%) rename {src => lib}/rules/media-feature-parentheses-space-inside/README.md (100%) rename {src => lib}/rules/media-feature-parentheses-space-inside/__tests__/index.js (95%) rename {src => lib}/rules/media-feature-parentheses-space-inside/index.js (76%) rename {src => lib}/rules/media-feature-range-operator-space-after/README.md (100%) rename {src => lib}/rules/media-feature-range-operator-space-after/__tests__/index.js (94%) rename {src => lib}/rules/media-feature-range-operator-space-after/index.js (56%) rename {src => lib}/rules/media-feature-range-operator-space-before/README.md (100%) rename {src => lib}/rules/media-feature-range-operator-space-before/__tests__/index.js (94%) rename {src => lib}/rules/media-feature-range-operator-space-before/index.js (57%) rename {src => lib}/rules/media-query-list-comma-newline-after/README.md (100%) rename {src => lib}/rules/media-query-list-comma-newline-after/__tests__/index.js (97%) rename {src => lib}/rules/media-query-list-comma-newline-after/index.js (53%) rename {src => lib}/rules/media-query-list-comma-newline-before/README.md (100%) rename {src => lib}/rules/media-query-list-comma-newline-before/__tests__/index.js (97%) create mode 100644 lib/rules/media-query-list-comma-newline-before/index.js rename {src => lib}/rules/media-query-list-comma-space-after/README.md (100%) rename {src => lib}/rules/media-query-list-comma-space-after/__tests__/index.js (97%) create mode 100644 lib/rules/media-query-list-comma-space-after/index.js rename {src => lib}/rules/media-query-list-comma-space-before/README.md (100%) rename {src => lib}/rules/media-query-list-comma-space-before/__tests__/index.js (97%) rename {src => lib}/rules/media-query-list-comma-space-before/index.js (51%) create mode 100644 lib/rules/mediaFeatureColonSpaceChecker.js create mode 100644 lib/rules/mediaQueryListCommaWhitespaceChecker.js rename {src => lib}/rules/no-browser-hacks/README.md (100%) rename {src => lib}/rules/no-browser-hacks/__tests__/index.js (87%) rename {src => lib}/rules/no-browser-hacks/index.js (60%) rename {src => lib}/rules/no-descending-specificity/README.md (100%) rename {src => lib}/rules/no-descending-specificity/__tests__/index.js (93%) rename {src => lib}/rules/no-descending-specificity/index.js (62%) rename {src => lib}/rules/no-duplicate-selectors/README.md (100%) rename {src => lib}/rules/no-duplicate-selectors/__tests__/fixtures/also-using-foo.css (100%) rename {src => lib}/rules/no-duplicate-selectors/__tests__/fixtures/using-foo-twice.css (100%) rename {src => lib}/rules/no-duplicate-selectors/__tests__/fixtures/using-foo.css (100%) rename {src => lib}/rules/no-duplicate-selectors/__tests__/index.js (73%) rename {src => lib}/rules/no-duplicate-selectors/index.js (64%) rename {src => lib}/rules/no-empty-source/README.md (100%) rename {src => lib}/rules/no-empty-source/__tests__/index.js (88%) create mode 100644 lib/rules/no-empty-source/index.js rename {src => lib}/rules/no-eol-whitespace/README.md (100%) rename {src => lib}/rules/no-eol-whitespace/__tests__/index.js (97%) rename {src => lib}/rules/no-eol-whitespace/index.js (58%) rename {src => lib}/rules/no-extra-semicolons/README.md (100%) rename {src => lib}/rules/no-extra-semicolons/__tests__/index.js (98%) rename {src => lib}/rules/no-extra-semicolons/index.js (62%) rename {src => lib}/rules/no-indistinguishable-colors/README.md (100%) rename {src => lib}/rules/no-indistinguishable-colors/__tests__/index.js (90%) rename {src => lib}/rules/no-indistinguishable-colors/index.js (51%) rename {src => lib}/rules/no-invalid-double-slash-comments/README.md (100%) rename {src => lib}/rules/no-invalid-double-slash-comments/__tests__/index.js (90%) rename {src => lib}/rules/no-invalid-double-slash-comments/index.js (60%) rename {src => lib}/rules/no-missing-end-of-source-newline/README.md (100%) rename {src => lib}/rules/no-missing-end-of-source-newline/__tests__/index.js (81%) create mode 100644 lib/rules/no-missing-end-of-source-newline/index.js rename {src => lib}/rules/no-unknown-animations/README.md (100%) rename {src => lib}/rules/no-unknown-animations/__tests__/index.js (97%) create mode 100644 lib/rules/no-unknown-animations/index.js rename {src => lib}/rules/no-unsupported-browser-features/README.md (100%) rename {src => lib}/rules/no-unsupported-browser-features/__tests__/index.js (89%) rename {src => lib}/rules/no-unsupported-browser-features/index.js (74%) rename {src => lib}/rules/number-leading-zero/README.md (100%) rename {src => lib}/rules/number-leading-zero/__tests__/index.js (97%) rename {src => lib}/rules/number-leading-zero/index.js (59%) rename {src => lib}/rules/number-max-precision/README.md (100%) rename {src => lib}/rules/number-max-precision/__tests__/index.js (92%) create mode 100644 lib/rules/number-max-precision/index.js rename {src => lib}/rules/number-no-trailing-zeros/README.md (100%) rename {src => lib}/rules/number-no-trailing-zeros/__tests__/index.js (92%) create mode 100644 lib/rules/number-no-trailing-zeros/index.js rename {src => lib}/rules/property-blacklist/README.md (100%) rename {src => lib}/rules/property-blacklist/__tests__/index.js (85%) create mode 100644 lib/rules/property-blacklist/index.js rename {src => lib}/rules/property-case/README.md (100%) rename {src => lib}/rules/property-case/__tests__/index.js (98%) create mode 100644 lib/rules/property-case/index.js rename {src => lib}/rules/property-no-unknown/README.md (100%) rename {src => lib}/rules/property-no-unknown/__tests__/index.js (94%) create mode 100644 lib/rules/property-no-unknown/index.js rename {src => lib}/rules/property-no-vendor-prefix/README.md (100%) rename {src => lib}/rules/property-no-vendor-prefix/__tests__/index.js (91%) create mode 100644 lib/rules/property-no-vendor-prefix/index.js rename {src => lib}/rules/property-whitelist/README.md (100%) rename {src => lib}/rules/property-whitelist/__tests__/index.js (86%) create mode 100644 lib/rules/property-whitelist/index.js rename {src => lib}/rules/root-no-standard-properties/README.md (100%) rename {src => lib}/rules/root-no-standard-properties/__tests__/index.js (93%) create mode 100644 lib/rules/root-no-standard-properties/index.js rename {src => lib}/rules/rule-nested-empty-line-before/README.md (100%) rename {src => lib}/rules/rule-nested-empty-line-before/__tests__/index.js (98%) create mode 100644 lib/rules/rule-nested-empty-line-before/index.js rename {src => lib}/rules/rule-non-nested-empty-line-before/README.md (100%) rename {src => lib}/rules/rule-non-nested-empty-line-before/__tests__/index.js (96%) create mode 100644 lib/rules/rule-non-nested-empty-line-before/index.js rename {src => lib}/rules/selector-attribute-brackets-space-inside/README.md (100%) rename {src => lib}/rules/selector-attribute-brackets-space-inside/__tests__/index.js (98%) rename {src => lib}/rules/selector-attribute-brackets-space-inside/index.js (70%) rename {src => lib}/rules/selector-attribute-operator-blacklist/README.md (100%) rename {src => lib}/rules/selector-attribute-operator-blacklist/__tests__/index.js (81%) create mode 100644 lib/rules/selector-attribute-operator-blacklist/index.js rename {src => lib}/rules/selector-attribute-operator-space-after/README.md (100%) rename {src => lib}/rules/selector-attribute-operator-space-after/__tests__/index.js (99%) create mode 100644 lib/rules/selector-attribute-operator-space-after/index.js rename {src => lib}/rules/selector-attribute-operator-space-before/README.md (100%) rename {src => lib}/rules/selector-attribute-operator-space-before/__tests__/index.js (99%) create mode 100644 lib/rules/selector-attribute-operator-space-before/index.js rename {src => lib}/rules/selector-attribute-operator-whitelist/README.md (100%) rename {src => lib}/rules/selector-attribute-operator-whitelist/__tests__/index.js (83%) create mode 100644 lib/rules/selector-attribute-operator-whitelist/index.js rename {src => lib}/rules/selector-attribute-quotes/README.md (100%) rename {src => lib}/rules/selector-attribute-quotes/__tests__/index.js (95%) create mode 100644 lib/rules/selector-attribute-quotes/index.js rename {src => lib}/rules/selector-class-pattern/README.md (100%) rename {src => lib}/rules/selector-class-pattern/__tests__/index.js (92%) rename {src => lib}/rules/selector-class-pattern/index.js (52%) rename {src => lib}/rules/selector-combinator-space-after/README.md (100%) rename {src => lib}/rules/selector-combinator-space-after/__tests__/index.js (96%) create mode 100644 lib/rules/selector-combinator-space-after/index.js rename {src => lib}/rules/selector-combinator-space-before/README.md (100%) rename {src => lib}/rules/selector-combinator-space-before/__tests__/index.js (96%) create mode 100644 lib/rules/selector-combinator-space-before/index.js rename {src => lib}/rules/selector-descendant-combinator-no-non-space/README.md (100%) rename {src => lib}/rules/selector-descendant-combinator-no-non-space/__tests__/index.js (80%) create mode 100644 lib/rules/selector-descendant-combinator-no-non-space/index.js rename {src => lib}/rules/selector-id-pattern/README.md (100%) rename {src => lib}/rules/selector-id-pattern/__tests__/index.js (77%) create mode 100644 lib/rules/selector-id-pattern/index.js rename {src => lib}/rules/selector-list-comma-newline-after/README.md (100%) rename {src => lib}/rules/selector-list-comma-newline-after/__tests__/index.js (96%) create mode 100644 lib/rules/selector-list-comma-newline-after/index.js rename {src => lib}/rules/selector-list-comma-newline-before/README.md (100%) rename {src => lib}/rules/selector-list-comma-newline-before/__tests__/index.js (94%) create mode 100644 lib/rules/selector-list-comma-newline-before/index.js rename {src => lib}/rules/selector-list-comma-space-after/README.md (100%) rename {src => lib}/rules/selector-list-comma-space-after/__tests__/index.js (95%) create mode 100644 lib/rules/selector-list-comma-space-after/index.js rename {src => lib}/rules/selector-list-comma-space-before/README.md (100%) rename {src => lib}/rules/selector-list-comma-space-before/__tests__/index.js (95%) rename {src => lib}/rules/selector-list-comma-space-before/index.js (51%) rename {src => lib}/rules/selector-max-compound-selectors/README.md (100%) rename {src => lib}/rules/selector-max-compound-selectors/__tests__/index.js (96%) rename {src => lib}/rules/selector-max-compound-selectors/index.js (63%) rename {src => lib}/rules/selector-max-empty-lines/README.md (100%) rename {src => lib}/rules/selector-max-empty-lines/__tests__/index.js (99%) create mode 100644 lib/rules/selector-max-empty-lines/index.js rename {src => lib}/rules/selector-max-specificity/README.md (100%) rename {src => lib}/rules/selector-max-specificity/__tests__/index.js (95%) rename {src => lib}/rules/selector-max-specificity/index.js (52%) rename {src => lib}/rules/selector-nested-pattern/README.md (100%) rename {src => lib}/rules/selector-nested-pattern/__tests__/index.js (82%) create mode 100644 lib/rules/selector-nested-pattern/index.js rename {src => lib}/rules/selector-no-attribute/README.md (100%) rename {src => lib}/rules/selector-no-attribute/__tests__/index.js (79%) create mode 100644 lib/rules/selector-no-attribute/index.js rename {src => lib}/rules/selector-no-combinator/README.md (100%) rename {src => lib}/rules/selector-no-combinator/__tests__/index.js (94%) create mode 100644 lib/rules/selector-no-combinator/index.js rename {src => lib}/rules/selector-no-empty/README.md (100%) rename {src => lib}/rules/selector-no-empty/__tests__/index.js (93%) create mode 100644 lib/rules/selector-no-empty/index.js rename {src => lib}/rules/selector-no-id/README.md (100%) rename {src => lib}/rules/selector-no-id/__tests__/index.js (93%) create mode 100644 lib/rules/selector-no-id/index.js rename {src => lib}/rules/selector-no-qualifying-type/README.md (100%) rename {src => lib}/rules/selector-no-qualifying-type/__tests__/index.js (99%) rename {src => lib}/rules/selector-no-qualifying-type/index.js (55%) rename {src => lib}/rules/selector-no-type/README.md (100%) rename {src => lib}/rules/selector-no-type/__tests__/index.js (94%) create mode 100644 lib/rules/selector-no-type/index.js rename {src => lib}/rules/selector-no-universal/README.md (100%) rename {src => lib}/rules/selector-no-universal/__tests__/index.js (77%) create mode 100644 lib/rules/selector-no-universal/index.js rename {src => lib}/rules/selector-no-vendor-prefix/README.md (100%) rename {src => lib}/rules/selector-no-vendor-prefix/__tests__/index.js (88%) create mode 100644 lib/rules/selector-no-vendor-prefix/index.js rename {src => lib}/rules/selector-pseudo-class-blacklist/README.md (100%) rename {src => lib}/rules/selector-pseudo-class-blacklist/__tests__/index.js (86%) create mode 100644 lib/rules/selector-pseudo-class-blacklist/index.js rename {src => lib}/rules/selector-pseudo-class-case/README.md (100%) rename {src => lib}/rules/selector-pseudo-class-case/__tests__/index.js (98%) create mode 100644 lib/rules/selector-pseudo-class-case/index.js rename {src => lib}/rules/selector-pseudo-class-no-unknown/README.md (100%) rename {src => lib}/rules/selector-pseudo-class-no-unknown/__tests__/index.js (92%) create mode 100644 lib/rules/selector-pseudo-class-no-unknown/index.js rename {src => lib}/rules/selector-pseudo-class-parentheses-space-inside/README.md (100%) rename {src => lib}/rules/selector-pseudo-class-parentheses-space-inside/__tests__/index.js (96%) rename {src => lib}/rules/selector-pseudo-class-parentheses-space-inside/index.js (68%) rename {src => lib}/rules/selector-pseudo-class-whitelist/README.md (100%) rename {src => lib}/rules/selector-pseudo-class-whitelist/__tests__/index.js (86%) create mode 100644 lib/rules/selector-pseudo-class-whitelist/index.js rename {src => lib}/rules/selector-pseudo-element-case/README.md (100%) rename {src => lib}/rules/selector-pseudo-element-case/__tests__/index.js (98%) create mode 100644 lib/rules/selector-pseudo-element-case/index.js rename {src => lib}/rules/selector-pseudo-element-colon-notation/README.md (100%) rename {src => lib}/rules/selector-pseudo-element-colon-notation/__tests__/index.js (93%) create mode 100644 lib/rules/selector-pseudo-element-colon-notation/index.js rename {src => lib}/rules/selector-pseudo-element-no-unknown/README.md (100%) rename {src => lib}/rules/selector-pseudo-element-no-unknown/__tests__/index.js (93%) create mode 100644 lib/rules/selector-pseudo-element-no-unknown/index.js rename {src => lib}/rules/selector-root-no-composition/README.md (100%) rename {src => lib}/rules/selector-root-no-composition/__tests__/index.js (80%) create mode 100644 lib/rules/selector-root-no-composition/index.js rename {src => lib}/rules/selector-type-case/README.md (100%) rename {src => lib}/rules/selector-type-case/__tests__/index.js (94%) create mode 100644 lib/rules/selector-type-case/index.js rename {src => lib}/rules/selector-type-no-unknown/README.md (100%) rename {src => lib}/rules/selector-type-no-unknown/__tests__/index.js (94%) create mode 100644 lib/rules/selector-type-no-unknown/index.js create mode 100644 lib/rules/selectorAttributeOperatorSpaceChecker.js create mode 100644 lib/rules/selectorCombinatorSpaceChecker.js create mode 100644 lib/rules/selectorListCommaWhitespaceChecker.js rename {src => lib}/rules/shorthand-property-no-redundant-values/README.md (100%) rename {src => lib}/rules/shorthand-property-no-redundant-values/__tests__/index.js (97%) create mode 100644 lib/rules/shorthand-property-no-redundant-values/index.js rename {src => lib}/rules/string-no-newline/README.md (100%) rename {src => lib}/rules/string-no-newline/__tests__/index.js (85%) rename {src => lib}/rules/string-no-newline/index.js (52%) rename {src => lib}/rules/string-quotes/README.md (100%) rename {src => lib}/rules/string-quotes/__tests__/index.js (94%) create mode 100644 lib/rules/string-quotes/index.js rename {src => lib}/rules/stylelint-disable-reason/README.md (100%) rename {src => lib}/rules/stylelint-disable-reason/__tests__/index.js (92%) rename {src => lib}/rules/stylelint-disable-reason/index.js (66%) rename {src => lib}/rules/time-no-imperceptible/README.md (100%) rename {src => lib}/rules/time-no-imperceptible/__tests__/index.js (96%) rename {src => lib}/rules/time-no-imperceptible/index.js (50%) rename {src => lib}/rules/unit-blacklist/README.md (100%) rename {src => lib}/rules/unit-blacklist/__tests__/index.js (93%) create mode 100644 lib/rules/unit-blacklist/index.js rename {src => lib}/rules/unit-case/README.md (100%) rename {src => lib}/rules/unit-case/__tests__/index.js (98%) create mode 100644 lib/rules/unit-case/index.js rename {src => lib}/rules/unit-no-unknown/README.md (100%) rename {src => lib}/rules/unit-no-unknown/__tests__/index.js (97%) create mode 100644 lib/rules/unit-no-unknown/index.js rename {src => lib}/rules/unit-whitelist/README.md (100%) rename {src => lib}/rules/unit-whitelist/__tests__/index.js (93%) create mode 100644 lib/rules/unit-whitelist/index.js rename {src => lib}/rules/value-keyword-case/README.md (100%) rename {src => lib}/rules/value-keyword-case/__tests__/index.js (99%) create mode 100644 lib/rules/value-keyword-case/index.js rename {src => lib}/rules/value-list-comma-newline-after/README.md (100%) rename {src => lib}/rules/value-list-comma-newline-after/__tests__/index.js (94%) create mode 100644 lib/rules/value-list-comma-newline-after/index.js rename {src => lib}/rules/value-list-comma-newline-before/README.md (100%) rename {src => lib}/rules/value-list-comma-newline-before/__tests__/index.js (95%) create mode 100644 lib/rules/value-list-comma-newline-before/index.js rename {src => lib}/rules/value-list-comma-space-after/README.md (100%) rename {src => lib}/rules/value-list-comma-space-after/__tests__/index.js (96%) create mode 100644 lib/rules/value-list-comma-space-after/index.js rename {src => lib}/rules/value-list-comma-space-before/README.md (100%) rename {src => lib}/rules/value-list-comma-space-before/__tests__/index.js (96%) rename {src => lib}/rules/value-list-comma-space-before/index.js (51%) rename {src => lib}/rules/value-list-max-empty-lines/README.md (100%) rename {src => lib}/rules/value-list-max-empty-lines/__tests__/index.js (94%) create mode 100644 lib/rules/value-list-max-empty-lines/index.js rename {src => lib}/rules/value-no-vendor-prefix/README.md (100%) rename {src => lib}/rules/value-no-vendor-prefix/__tests__/index.js (92%) create mode 100644 lib/rules/value-no-vendor-prefix/index.js create mode 100644 lib/rules/valueListCommaWhitespaceChecker.js create mode 100644 lib/standalone.js rename {src => lib}/testUtils/__tests__/mergeTestDescriptions-test.js (89%) create mode 100644 lib/testUtils/basicChecks.js rename {src => lib}/testUtils/createRuleTester.js (90%) create mode 100644 lib/testUtils/mergeTestDescriptions.js create mode 100644 lib/testUtils/testRule.js rename {src => lib}/utils/__tests__/atRuleParamIndex-test.js (80%) rename {src => lib}/utils/__tests__/beforeBlockString-test.js (64%) rename {src => lib}/utils/__tests__/blockString-test.js (61%) rename {src => lib}/utils/__tests__/blurComments-test.js (67%) rename {src => lib}/utils/__tests__/blurFunctionArguments-test.js (80%) rename {src => lib}/utils/__tests__/blurInterpolation-test.js (59%) create mode 100644 lib/utils/__tests__/containsString-test.js rename {src => lib}/utils/__tests__/declarationValueIndex-test.js (79%) create mode 100644 lib/utils/__tests__/findAnimationName-test.js rename {src => lib}/utils/__tests__/findAtRuleContext-test.js (83%) create mode 100644 lib/utils/__tests__/findFontFamily-test.js create mode 100644 lib/utils/__tests__/findListStyleType-test.js rename {src => lib}/utils/__tests__/fixtures/one.css (100%) rename {src => lib}/utils/__tests__/fixtures/two.css (100%) rename {src => lib}/utils/__tests__/functionArgumentsSearch-test.js (93%) rename {src => lib}/utils/__tests__/getUnitFromValueNode-test.js (90%) rename {src => lib}/utils/__tests__/hasBlock-test.js (93%) rename {src => lib}/utils/__tests__/hasEmptyBlock-test.js (93%) rename {src => lib}/utils/__tests__/hasEmptyLine-test.js (81%) rename {src => lib}/utils/__tests__/hasInterpolation-test.js (90%) rename {src => lib}/utils/__tests__/isCounterIncrementCustomIdentValue-test.js (83%) rename {src => lib}/utils/__tests__/isCustomMediaQuery-test.js (79%) rename {src => lib}/utils/__tests__/isCustomProperty-test.js (79%) rename {src => lib}/utils/__tests__/isCustomPropertySet-test.js (66%) rename {src => lib}/utils/__tests__/isKeyframeRule-test.js (89%) rename {src => lib}/utils/__tests__/isKeyframeSelector-test.js (87%) rename {src => lib}/utils/__tests__/isNumbery-test.js (71%) rename {src => lib}/utils/__tests__/isOnlyWhitespace-test.js (66%) rename {src => lib}/utils/__tests__/isRangeContextMediaFeature-test.js (84%) rename {src => lib}/utils/__tests__/isSingleLineString-test.js (65%) rename {src => lib}/utils/__tests__/isStandardSyntaxAtRule-test.js (88%) rename {src => lib}/utils/__tests__/isStandardSyntaxDeclaration-test.js (93%) rename {src => lib}/utils/__tests__/isStandardSyntaxFunction-test.js (70%) rename {src => lib}/utils/__tests__/isStandardSyntaxMediaFeature-test.js (85%) rename {src => lib}/utils/__tests__/isStandardSyntaxMediaFeatureName-test.js (91%) rename {src => lib}/utils/__tests__/isStandardSyntaxProperty-test.js (82%) rename {src => lib}/utils/__tests__/isStandardSyntaxRule-test.js (93%) rename {src => lib}/utils/__tests__/isStandardSyntaxSelector-test.js (89%) rename {src => lib}/utils/__tests__/isStandardSyntaxTypeSelector-test.js (85%) rename {src => lib}/utils/__tests__/isStandardSyntaxUrl-test.js (77%) rename {src => lib}/utils/__tests__/isStandardSyntaxValue-test.js (82%) rename {src => lib}/utils/__tests__/isValidFontSize-test.js (74%) rename {src => lib}/utils/__tests__/isValidHex-test.js (84%) rename {src => lib}/utils/__tests__/isVariable-test.js (83%) rename {src => lib}/utils/__tests__/matchesStringOrRegExp-test.js (56%) rename {src => lib}/utils/__tests__/nextNonCommentNode-test.js (87%) create mode 100644 lib/utils/__tests__/nodeContextLookup-test.js rename {src => lib}/utils/__tests__/optionsMatches-test.js (92%) rename {src => lib}/utils/__tests__/report-test.js (91%) rename {src => lib}/utils/__tests__/ruleMessages-test.js (68%) rename {src => lib}/utils/__tests__/validateObjectWithStringArrayProps-test.js (81%) rename {src => lib}/utils/__tests__/validateOptions-test.js (93%) rename {src => lib}/utils/atRuleParamIndex.js (51%) create mode 100644 lib/utils/beforeBlockString.js rename {src => lib}/utils/blockString.js (55%) create mode 100644 lib/utils/blurComments.js rename {src => lib}/utils/blurFunctionArguments.js (75%) create mode 100644 lib/utils/blurInterpolation.js rename {src => lib}/utils/configurationError.js (81%) rename {src => lib}/utils/containsString.js (93%) rename {src => lib}/utils/declarationValueIndex.js (84%) create mode 100644 lib/utils/findAnimationName.js rename {src => lib}/utils/findAtRuleContext.js (64%) rename {src => lib}/utils/findFontFamily.js (52%) create mode 100644 lib/utils/findListStyleType.js rename {src => lib}/utils/functionArgumentsSearch.js (75%) create mode 100644 lib/utils/getIsFileIgnored.js create mode 100644 lib/utils/getModulePath.js create mode 100644 lib/utils/getUnitFromValueNode.js rename {src => lib}/utils/hasBlock.js (69%) rename {src => lib}/utils/hasEmptyBlock.js (55%) create mode 100644 lib/utils/hasEmptyLine.js create mode 100644 lib/utils/hasInterpolation.js rename {src => lib}/utils/hasLessInterpolation.js (62%) rename {src => lib}/utils/hasPsvInterpolation.js (65%) rename {src => lib}/utils/hasScssInterpolation.js (62%) rename {src => lib}/utils/isAutoprefixable.js (76%) create mode 100644 lib/utils/isCounterIncrementCustomIdentValue.js rename {src => lib}/utils/isCustomMediaQuery.js (59%) rename {src => lib}/utils/isCustomProperty.js (60%) create mode 100644 lib/utils/isCustomPropertySet.js create mode 100644 lib/utils/isKeyframeRule.js create mode 100644 lib/utils/isKeyframeSelector.js rename {src => lib}/utils/isNumbery.js (63%) rename {src => lib}/utils/isOnlyWhitespace.js (77%) create mode 100644 lib/utils/isRangeContextMediaFeature.js rename {src => lib}/utils/isSingleLineString.js (78%) rename {src => lib}/utils/isStandardSyntaxAtRule.js (66%) rename {src => lib}/utils/isStandardSyntaxDeclaration.js (60%) rename {src => lib}/utils/isStandardSyntaxFunction.js (74%) rename {src => lib}/utils/isStandardSyntaxMediaFeature.js (58%) rename {src => lib}/utils/isStandardSyntaxMediaFeatureName.js (61%) rename {src => lib}/utils/isStandardSyntaxProperty.js (52%) rename {src => lib}/utils/isStandardSyntaxRule.js (63%) create mode 100644 lib/utils/isStandardSyntaxSelector.js rename {src => lib}/utils/isStandardSyntaxTypeSelector.js (53%) create mode 100644 lib/utils/isStandardSyntaxUrl.js create mode 100644 lib/utils/isStandardSyntaxValue.js create mode 100644 lib/utils/isValidFontSize.js rename {src => lib}/utils/isValidHex.js (51%) rename {src => lib}/utils/isVariable.js (61%) rename {src => lib}/utils/isWhitespace.js (78%) rename {src => lib}/utils/matchesStringOrRegExp.js (80%) rename {src => lib}/utils/nextNonCommentNode.js (83%) rename {src => lib}/utils/nodeContextLookup.js (86%) rename {src => lib}/utils/optionsMatches.js (50%) create mode 100644 lib/utils/parseSelector.js rename {src => lib}/utils/rawNodeString.js (84%) rename {src => lib}/utils/report.js (64%) rename {src => lib}/utils/ruleMessages.js (64%) rename {src => lib}/utils/validateObjectWithStringArrayProps.js (66%) rename {src => lib}/utils/validateOptions.js (68%) rename {src => lib}/utils/whitespaceChecker.js (66%) create mode 100755 scripts/rules-tape.js delete mode 100644 src/__tests__/fixtures/plugin-array.js delete mode 100644 src/__tests__/message.test.js delete mode 100644 src/__tests__/needlessDisables.test.js delete mode 100644 src/createPlugin.js delete mode 100644 src/formatters/__tests__/needlessDisablesStringFormatter-test.js delete mode 100644 src/formatters/index.js delete mode 100644 src/getConfigForFile.js delete mode 100644 src/getPostcssResult.js delete mode 100644 src/index.js delete mode 100644 src/postcssPlugin.js delete mode 100644 src/reference/keywordSets.js delete mode 100644 src/reference/namedColorData.js delete mode 100644 src/reference/propertySets.js delete mode 100644 src/reference/punctuationSets.js delete mode 100644 src/reference/shorthandData.js delete mode 100644 src/rules/at-rule-blacklist/index.js delete mode 100644 src/rules/at-rule-empty-line-before/index.js delete mode 100644 src/rules/at-rule-name-case/index.js delete mode 100644 src/rules/at-rule-name-newline-after/index.js delete mode 100644 src/rules/at-rule-name-space-after/index.js delete mode 100644 src/rules/at-rule-no-unknown/index.js delete mode 100644 src/rules/at-rule-no-vendor-prefix/index.js delete mode 100644 src/rules/at-rule-semicolon-newline-after/index.js delete mode 100644 src/rules/at-rule-whitelist/index.js delete mode 100644 src/rules/block-closing-brace-empty-line-before/index.js delete mode 100644 src/rules/block-no-single-line/index.js delete mode 100644 src/rules/color-named/index.js delete mode 100644 src/rules/comment-empty-line-before/index.js delete mode 100644 src/rules/comment-no-empty/index.js delete mode 100644 src/rules/comment-word-blacklist/index.js delete mode 100644 src/rules/custom-media-pattern/index.js delete mode 100644 src/rules/custom-property-empty-line-before/index.js delete mode 100644 src/rules/custom-property-no-outside-root/index.js delete mode 100644 src/rules/custom-property-pattern/index.js delete mode 100644 src/rules/declaration-bang-space-after/index.js delete mode 100644 src/rules/declaration-bang-space-before/index.js delete mode 100644 src/rules/declaration-block-no-redundant-longhand-properties/index.js delete mode 100644 src/rules/declaration-block-no-shorthand-property-overrides/index.js delete mode 100644 src/rules/declaration-block-single-line-max-declarations/index.js delete mode 100644 src/rules/declaration-block-trailing-semicolon/index.js delete mode 100644 src/rules/declaration-colon-space-after/index.js delete mode 100644 src/rules/declaration-colon-space-before/index.js delete mode 100644 src/rules/declaration-empty-line-before/index.js delete mode 100644 src/rules/declaration-no-important/index.js delete mode 100644 src/rules/declaration-property-unit-blacklist/index.js delete mode 100644 src/rules/declaration-property-unit-whitelist/index.js delete mode 100644 src/rules/declaration-property-value-blacklist/index.js delete mode 100644 src/rules/declaration-property-value-whitelist/index.js delete mode 100644 src/rules/function-blacklist/index.js delete mode 100644 src/rules/function-comma-newline-after/index.js delete mode 100644 src/rules/function-comma-space-after/index.js delete mode 100644 src/rules/function-max-empty-lines/index.js delete mode 100644 src/rules/function-url-data-uris/index.js delete mode 100644 src/rules/function-url-no-scheme-relative/index.js delete mode 100644 src/rules/function-url-scheme-whitelist/index.js delete mode 100644 src/rules/function-whitelist/index.js delete mode 100644 src/rules/indentation/__tests__/at-rules.js delete mode 100644 src/rules/indentation/__tests__/comments.js delete mode 100644 src/rules/indentation/__tests__/functions.js delete mode 100644 src/rules/indentation/__tests__/rules.js delete mode 100644 src/rules/indentation/__tests__/selectors.js delete mode 100644 src/rules/index.js delete mode 100644 src/rules/keyframe-declaration-no-important/index.js delete mode 100644 src/rules/media-feature-colon-space-after/index.js delete mode 100644 src/rules/media-feature-colon-space-before/index.js delete mode 100644 src/rules/media-feature-name-blacklist/index.js delete mode 100644 src/rules/media-feature-name-case/index.js delete mode 100644 src/rules/media-feature-name-no-unknown/index.js delete mode 100644 src/rules/media-feature-name-no-vendor-prefix/index.js delete mode 100644 src/rules/media-feature-name-whitelist/index.js delete mode 100644 src/rules/media-query-list-comma-newline-before/index.js delete mode 100644 src/rules/media-query-list-comma-space-after/index.js delete mode 100644 src/rules/no-empty-source/index.js delete mode 100644 src/rules/no-missing-end-of-source-newline/index.js delete mode 100644 src/rules/no-unknown-animations/index.js delete mode 100644 src/rules/number-max-precision/index.js delete mode 100644 src/rules/number-no-trailing-zeros/index.js delete mode 100644 src/rules/property-blacklist/index.js delete mode 100644 src/rules/property-case/index.js delete mode 100644 src/rules/property-no-unknown/index.js delete mode 100644 src/rules/property-no-vendor-prefix/index.js delete mode 100644 src/rules/property-whitelist/index.js delete mode 100644 src/rules/root-no-standard-properties/index.js delete mode 100644 src/rules/rule-nested-empty-line-before/index.js delete mode 100644 src/rules/rule-non-nested-empty-line-before/index.js delete mode 100644 src/rules/selector-attribute-operator-blacklist/index.js delete mode 100644 src/rules/selector-attribute-operator-space-after/index.js delete mode 100644 src/rules/selector-attribute-operator-space-before/index.js delete mode 100644 src/rules/selector-attribute-operator-whitelist/index.js delete mode 100644 src/rules/selector-attribute-quotes/index.js delete mode 100644 src/rules/selector-combinator-space-after/index.js delete mode 100644 src/rules/selector-combinator-space-before/index.js delete mode 100644 src/rules/selector-descendant-combinator-no-non-space/index.js delete mode 100644 src/rules/selector-id-pattern/index.js delete mode 100644 src/rules/selector-list-comma-newline-after/index.js delete mode 100644 src/rules/selector-list-comma-newline-before/index.js delete mode 100644 src/rules/selector-list-comma-space-after/index.js delete mode 100644 src/rules/selector-max-empty-lines/index.js delete mode 100644 src/rules/selector-nested-pattern/index.js delete mode 100644 src/rules/selector-no-attribute/index.js delete mode 100644 src/rules/selector-no-combinator/index.js delete mode 100644 src/rules/selector-no-empty/index.js delete mode 100644 src/rules/selector-no-id/index.js delete mode 100644 src/rules/selector-no-type/index.js delete mode 100644 src/rules/selector-no-universal/index.js delete mode 100644 src/rules/selector-no-vendor-prefix/index.js delete mode 100644 src/rules/selector-pseudo-class-blacklist/index.js delete mode 100644 src/rules/selector-pseudo-class-case/index.js delete mode 100644 src/rules/selector-pseudo-class-no-unknown/index.js delete mode 100644 src/rules/selector-pseudo-class-whitelist/index.js delete mode 100644 src/rules/selector-pseudo-element-case/index.js delete mode 100644 src/rules/selector-pseudo-element-colon-notation/index.js delete mode 100644 src/rules/selector-pseudo-element-no-unknown/index.js delete mode 100644 src/rules/selector-root-no-composition/index.js delete mode 100644 src/rules/selector-type-case/index.js delete mode 100644 src/rules/selector-type-no-unknown/index.js delete mode 100644 src/rules/shorthand-property-no-redundant-values/index.js delete mode 100644 src/rules/string-quotes/index.js delete mode 100644 src/rules/unit-blacklist/index.js delete mode 100644 src/rules/unit-case/index.js delete mode 100644 src/rules/unit-no-unknown/index.js delete mode 100644 src/rules/unit-whitelist/index.js delete mode 100644 src/rules/value-keyword-case/index.js delete mode 100644 src/rules/value-list-comma-newline-after/index.js delete mode 100644 src/rules/value-list-comma-newline-before/index.js delete mode 100644 src/rules/value-list-comma-space-after/index.js delete mode 100644 src/rules/value-list-max-empty-lines/index.js delete mode 100644 src/rules/value-no-vendor-prefix/index.js delete mode 100644 src/standalone.js delete mode 100644 src/testUtils/basicChecks.js delete mode 100644 src/testUtils/index.js delete mode 100644 src/testUtils/mergeTestDescriptions.js delete mode 100644 src/testUtils/testRule.js delete mode 100644 src/utils/__tests__/containsString-test.js delete mode 100644 src/utils/__tests__/findAnimationName-test.js delete mode 100644 src/utils/__tests__/findFontFamily-test.js delete mode 100644 src/utils/__tests__/findListStyleType-test.js delete mode 100644 src/utils/__tests__/nodeContextLookup-test.js delete mode 100644 src/utils/beforeBlockString.js delete mode 100644 src/utils/blurComments.js delete mode 100644 src/utils/blurInterpolation.js delete mode 100644 src/utils/findAnimationName.js delete mode 100644 src/utils/findListStyleType.js delete mode 100644 src/utils/getIsFileIgnored.js delete mode 100644 src/utils/getModulePath.js delete mode 100644 src/utils/getUnitFromValueNode.js delete mode 100644 src/utils/hasEmptyLine.js delete mode 100644 src/utils/hasInterpolation.js delete mode 100644 src/utils/index.js delete mode 100644 src/utils/isCounterIncrementCustomIdentValue.js delete mode 100644 src/utils/isCustomPropertySet.js delete mode 100644 src/utils/isKeyframeRule.js delete mode 100644 src/utils/isKeyframeSelector.js delete mode 100644 src/utils/isRangeContextMediaFeature.js delete mode 100644 src/utils/isStandardSyntaxSelector.js delete mode 100644 src/utils/isStandardSyntaxUrl.js delete mode 100644 src/utils/isStandardSyntaxValue.js delete mode 100644 src/utils/isValidFontSize.js delete mode 100644 src/utils/parseSelector.js diff --git a/.eslintignore b/.eslintignore index 048c68e05e..4933882514 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,2 @@ -dist .nyc_output decls diff --git a/.eslintrc b/.eslintrc index efd0c7b527..46e48b7ae1 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,21 +1,22 @@ { "extends": [ - "stylelint", - "plugin:flowtype/recommended" + "stylelint" ], + "parserOptions": { + "ecmaVersion": 6, + "sourceType": "script" + }, "env": { "jest": true }, - "parser": "babel-eslint", - "plugins": [ - "flowtype" - ], "rules": { "arrow-spacing": 2, "no-var": 2, "object-shorthand": 2, "prefer-const": 2, "sort-imports": 2, - "template-curly-spacing": 2 + "template-curly-spacing": 2, + "strict": ["error", "global"], + "spaced-comment": 0 } } diff --git a/.gitignore b/.gitignore index dbb06dbb4c..e93b3115d3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ node_modules -dist *.log .nyc_output diff --git a/.travis.yml b/.travis.yml index 624e52207e..fb1d3f3378 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,7 +9,7 @@ matrix: script: npm run lint && npm run flow env: CI=lint+flow - node_js: '6' - script: npm run jest + script: npm run jest-ci env: CI=jest - node_js: '7' script: npm run test diff --git a/bin/stylelint b/bin/stylelint new file mode 100755 index 0000000000..d2316ff6d6 --- /dev/null +++ b/bin/stylelint @@ -0,0 +1,3 @@ +#!/usr/bin/env node + +require('../lib/cli'); diff --git a/docs/developer-guide/plugins.md b/docs/developer-guide/plugins.md index 9b9b169d82..12ce4f0a1a 100644 --- a/docs/developer-guide/plugins.md +++ b/docs/developer-guide/plugins.md @@ -78,7 +78,7 @@ In addition to the standard parsers mentioned in the ["Working on rules"](rules. - [postcss-resolve-nested-selector](https://github.com/davidtheclark/postcss-resolve-nested-selector): Given a (nested) selector in a PostCSS AST, return an array of resolved selectors. - [style-search](https://github.com/davidtheclark/style-search): Search CSS (and CSS-like) strings, with sensitivity to whether matches occur inside strings, comments, and functions. -Have a look through [stylelint's internal utils](https://github.com/stylelint/stylelint/tree/master/src/utils) and if you come across one that you need in your plugin, then please consider helping us extract it out into a external module. +Have a look through [stylelint's internal utils](https://github.com/stylelint/stylelint/tree/master/lib/utils) and if you come across one that you need in your plugin, then please consider helping us extract it out into a external module. ## Testing plugins diff --git a/docs/developer-guide/rule-testers.md b/docs/developer-guide/rule-testers.md index d6ebc0f45a..732a23f9b9 100644 --- a/docs/developer-guide/rule-testers.md +++ b/docs/developer-guide/rule-testers.md @@ -10,7 +10,7 @@ When developing plugins, you can use the following rule testers or create your o - stylelint-test-rule-mocha - stylelint-test-rule-ava -The rule tester used within the stylelint codebase is at `src/testUtils/testRule.js`. (It is the same as `stylelint-test-rule-tape`.) +The rule tester used within the stylelint codebase is at `lib/testUtils/testRule.js`. (It is the same as `stylelint-test-rule-tape`.) ## Using a rule tester @@ -48,7 +48,7 @@ Required properties: Optional properties: - `syntax` {"css"|"less"|"scss"|"sugarss"}: Defaults to `"css"`. Other settings use special parsers. -- `skipBasicChecks` {boolean}: Defaults to `false`. If `true`, a few rudimentary checks (that should almost always be included) will not be performed. You can check those out in `src/testUtils/basicChecks.js`. +- `skipBasicChecks` {boolean}: Defaults to `false`. If `true`, a few rudimentary checks (that should almost always be included) will not be performed. You can check those out in `lib/testUtils/basicChecks.js`. - `preceedingPlugins` {array}: An array of PostCSS plugins that should be run before the CSS is tested. ## Creating a rule tester diff --git a/docs/developer-guide/rules.md b/docs/developer-guide/rules.md index 84250a7c15..c61d594fa7 100644 --- a/docs/developer-guide/rules.md +++ b/docs/developer-guide/rules.md @@ -98,7 +98,7 @@ You will use the simple [PostCSS API](http://api.postcss.org/) to navigate and a Depending on the rule, we also recommend using [postcss-value-parser](https://github.com/TrySound/postcss-value-parser) and [postcss-selector-parser](https://github.com/postcss/postcss-selector-parser). There are significant benefits to using these parsers instead of regular expressions or `indexOf` searches (even if they aren't always the most performant method). -stylelint has a number of [utility functions](https://github.com/stylelint/stylelint/tree/master/src/utils) that are used in existing rules and might prove useful to you, as well. Please look through those so that you know what's available. (And if you have a new function that you think might prove generally helpful, let's add it to the list!) +stylelint has a number of [utility functions](https://github.com/stylelint/stylelint/tree/master/lib/utils) that are used in existing rules and might prove useful to you, as well. Please look through those so that you know what's available. (And if you have a new function that you think might prove generally helpful, let's add it to the list!) In particular, you will definitely want to use `validateOptions()` so that users are warned about invalid options. (Looking at other rules for examples of options validation will help a lot.) @@ -147,7 +147,7 @@ However, this runs all 25,000+ unit tests and also linting. To run tests in a single file only (which you'll want to do during development), you'll need to use `babel-tape-runner` (because the codebase is ES6). For example, to run the test for the `color-hex-case` rule: ```console -./node_modules/.bin/babel-tape-runner src/rules/color-hex-case/__tests__/index.js +./node_modules/.bin/babel-tape-runner lib/rules/color-hex-case/__tests__/index.js ``` ### Write the README @@ -189,7 +189,7 @@ Take the form of: The final step is to add references to the new rule in the following places: -- [The rules `index.js` file](https://github.com/stylelint/stylelint/blob/master/src/rules/index.js) +- [The rules `index.js` file](https://github.com/stylelint/stylelint/blob/master/lib/rules/index.js) - [The list of rules](../user-guide/rules.md) - [The example config](../user-guide/example-config.md) diff --git a/docs/user-guide/css-processors.md b/docs/user-guide/css-processors.md index f6df59df57..0dbd73066e 100644 --- a/docs/user-guide/css-processors.md +++ b/docs/user-guide/css-processors.md @@ -39,7 +39,7 @@ postcss([ require("reporter") ]) .process(css, { - from: "src/app.css", + from: "lib/app.css", syntax: scss }) }) diff --git a/docs/user-guide/postcss-plugin.md b/docs/user-guide/postcss-plugin.md index 1e00eac767..45974d6a55 100644 --- a/docs/user-guide/postcss-plugin.md +++ b/docs/user-guide/postcss-plugin.md @@ -101,7 +101,7 @@ var postcss = require("postcss") var stylelint = require("stylelint") // CSS to be processed -var css = fs.readFileSync("src/app.css", "utf8") +var css = fs.readFileSync("lib/app.css", "utf8") postcss( processors: [ @@ -114,7 +114,7 @@ postcss( require("postcss-reporter")({ clearMessages: true }) ] ) - .process(css, { from: 'src/app.css', to: 'app.css' }) + .process(css, { from: 'lib/app.css', to: 'app.css' }) .then(function (result) { fs.writeFileSync('app.css', result.css); if ( result.map ) fs.writeFileSync('app.css.map', result.map); diff --git a/docs/user-guide/rules.md b/docs/user-guide/rules.md index 84de3f1603..6ad16ae8a9 100644 --- a/docs/user-guide/rules.md +++ b/docs/user-guide/rules.md @@ -10,259 +10,259 @@ Here are all the rules within stylelint, grouped by the [*thing*](http://apps.wo ### Color -- [`color-hex-case`](../../src/rules/color-hex-case/README.md): Specify lowercase or uppercase for hex colors. -- [`color-hex-length`](../../src/rules/color-hex-length/README.md): Specify short or long notation for hex colors. -- [`color-named`](../../src/rules/color-named/README.md): Require (where possible) or disallow named colors. -- [`color-no-hex`](../../src/rules/color-no-hex/README.md): Disallow hex colors. -- [`color-no-invalid-hex`](../../src/rules/color-no-invalid-hex/README.md): Disallow invalid hex colors. +- [`color-hex-case`](../../lib/rules/color-hex-case/README.md): Specify lowercase or uppercase for hex colors. +- [`color-hex-length`](../../lib/rules/color-hex-length/README.md): Specify short or long notation for hex colors. +- [`color-named`](../../lib/rules/color-named/README.md): Require (where possible) or disallow named colors. +- [`color-no-hex`](../../lib/rules/color-no-hex/README.md): Disallow hex colors. +- [`color-no-invalid-hex`](../../lib/rules/color-no-invalid-hex/README.md): Disallow invalid hex colors. ### Font family -- [`font-family-name-quotes`](../../src/rules/font-family-name-quotes/README.md): Specify whether or not quotation marks should be used around font family names. -- [`font-family-no-duplicate-names`](../../src/rules/font-family-no-duplicate-names/README.md): Disallow duplicate font family names. +- [`font-family-name-quotes`](../../lib/rules/font-family-name-quotes/README.md): Specify whether or not quotation marks should be used around font family names. +- [`font-family-no-duplicate-names`](../../lib/rules/font-family-no-duplicate-names/README.md): Disallow duplicate font family names. ### Font weight -- [`font-weight-notation`](../../src/rules/font-weight-notation/README.md): Require numeric or named (where possible) `font-weight` values. +- [`font-weight-notation`](../../lib/rules/font-weight-notation/README.md): Require numeric or named (where possible) `font-weight` values. ### Function -- [`function-blacklist`](../../src/rules/function-blacklist/README.md): Specify a blacklist of disallowed functions. -- [`function-calc-no-unspaced-operator`](../../src/rules/function-calc-no-unspaced-operator/README.md): Disallow an unspaced operator within `calc` functions. -- [`function-comma-newline-after`](../../src/rules/function-comma-newline-after/README.md): Require a newline or disallow whitespace after the commas of functions. -- [`function-comma-newline-before`](../../src/rules/function-comma-newline-before/README.md): Require a newline or disallow whitespace before the commas of functions. -- [`function-comma-space-after`](../../src/rules/function-comma-space-after/README.md): Require a single space or disallow whitespace after the commas of functions. -- [`function-comma-space-before`](../../src/rules/function-comma-space-before/README.md): Require a single space or disallow whitespace before the commas of functions. -- [`function-linear-gradient-no-nonstandard-direction`](../../src/rules/function-linear-gradient-no-nonstandard-direction/README.md): Disallow direction values in `linear-gradient()` calls that are not valid according to the [standard syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient#Syntax). -- [`function-max-empty-lines`](../../src/rules/function-max-empty-lines/README.md): Limit the number of adjacent empty lines within functions. -- [`function-name-case`](../../src/rules/function-name-case/README.md): Specify lowercase or uppercase for function names. -- [`function-parentheses-newline-inside`](../../src/rules/function-parentheses-newline-inside/README.md): Require a newline or disallow whitespace on the inside of the parentheses of functions. -- [`function-parentheses-space-inside`](../../src/rules/function-parentheses-space-inside/README.md): Require a single space or disallow whitespace on the inside of the parentheses of functions. -- [`function-url-data-uris`](../../src/rules/function-url-data-uris/README.md): Require or disallow data URIs for urls. -- [`function-url-no-scheme-relative`](../../src/rules/function-url-no-scheme-relative/README.md): Disallow scheme-relative urls. -- [`function-url-quotes`](../../src/rules/function-url-quotes/README.md): Require or disallow quotes for urls. -- [`function-url-scheme-whitelist`](../../src/rules/function-url-scheme-whitelist/README.md): Specify a whitelist of allowed url schemes. -- [`function-whitelist`](../../src/rules/function-whitelist/README.md): Specify a whitelist of allowed functions. -- [`function-whitespace-after`](../../src/rules/function-whitespace-after/README.md): Require or disallow whitespace after functions. +- [`function-blacklist`](../../lib/rules/function-blacklist/README.md): Specify a blacklist of disallowed functions. +- [`function-calc-no-unspaced-operator`](../../lib/rules/function-calc-no-unspaced-operator/README.md): Disallow an unspaced operator within `calc` functions. +- [`function-comma-newline-after`](../../lib/rules/function-comma-newline-after/README.md): Require a newline or disallow whitespace after the commas of functions. +- [`function-comma-newline-before`](../../lib/rules/function-comma-newline-before/README.md): Require a newline or disallow whitespace before the commas of functions. +- [`function-comma-space-after`](../../lib/rules/function-comma-space-after/README.md): Require a single space or disallow whitespace after the commas of functions. +- [`function-comma-space-before`](../../lib/rules/function-comma-space-before/README.md): Require a single space or disallow whitespace before the commas of functions. +- [`function-linear-gradient-no-nonstandard-direction`](../../lib/rules/function-linear-gradient-no-nonstandard-direction/README.md): Disallow direction values in `linear-gradient()` calls that are not valid according to the [standard syntax](https://developer.mozilla.org/en-US/docs/Web/CSS/linear-gradient#Syntax). +- [`function-max-empty-lines`](../../lib/rules/function-max-empty-lines/README.md): Limit the number of adjacent empty lines within functions. +- [`function-name-case`](../../lib/rules/function-name-case/README.md): Specify lowercase or uppercase for function names. +- [`function-parentheses-newline-inside`](../../lib/rules/function-parentheses-newline-inside/README.md): Require a newline or disallow whitespace on the inside of the parentheses of functions. +- [`function-parentheses-space-inside`](../../lib/rules/function-parentheses-space-inside/README.md): Require a single space or disallow whitespace on the inside of the parentheses of functions. +- [`function-url-data-uris`](../../lib/rules/function-url-data-uris/README.md): Require or disallow data URIs for urls. +- [`function-url-no-scheme-relative`](../../lib/rules/function-url-no-scheme-relative/README.md): Disallow scheme-relative urls. +- [`function-url-quotes`](../../lib/rules/function-url-quotes/README.md): Require or disallow quotes for urls. +- [`function-url-scheme-whitelist`](../../lib/rules/function-url-scheme-whitelist/README.md): Specify a whitelist of allowed url schemes. +- [`function-whitelist`](../../lib/rules/function-whitelist/README.md): Specify a whitelist of allowed functions. +- [`function-whitespace-after`](../../lib/rules/function-whitespace-after/README.md): Require or disallow whitespace after functions. ### Number -- [`number-leading-zero`](../../src/rules/number-leading-zero/README.md): Require or disallow a leading zero for fractional numbers less than 1. -- [`number-max-precision`](../../src/rules/number-max-precision/README.md): Limit the number of decimal places allowed in numbers. -- [`number-no-trailing-zeros`](../../src/rules/number-no-trailing-zeros/README.md): Disallow trailing zeros in numbers. +- [`number-leading-zero`](../../lib/rules/number-leading-zero/README.md): Require or disallow a leading zero for fractional numbers less than 1. +- [`number-max-precision`](../../lib/rules/number-max-precision/README.md): Limit the number of decimal places allowed in numbers. +- [`number-no-trailing-zeros`](../../lib/rules/number-no-trailing-zeros/README.md): Disallow trailing zeros in numbers. ### String -- [`string-no-newline`](../../src/rules/string-no-newline/README.md): Disallow (unescaped) newlines in strings. -- [`string-quotes`](../../src/rules/string-quotes/README.md): Specify single or double quotes around strings. +- [`string-no-newline`](../../lib/rules/string-no-newline/README.md): Disallow (unescaped) newlines in strings. +- [`string-quotes`](../../lib/rules/string-quotes/README.md): Specify single or double quotes around strings. ### Length -- [`length-zero-no-unit`](../../src/rules/length-zero-no-unit/README.md): Disallow units for zero lengths. +- [`length-zero-no-unit`](../../lib/rules/length-zero-no-unit/README.md): Disallow units for zero lengths. ### Time -- [`time-no-imperceptible`](../../src/rules/time-no-imperceptible/README.md): Disallow `animation` and `transition` less than or equal to 100ms. +- [`time-no-imperceptible`](../../lib/rules/time-no-imperceptible/README.md): Disallow `animation` and `transition` less than or equal to 100ms. ### Unit -- [`unit-blacklist`](../../src/rules/unit-blacklist/README.md): Specify a blacklist of disallowed units. -- [`unit-case`](../../src/rules/unit-case/README.md): Specify lowercase or uppercase for units. -- [`unit-no-unknown`](../../src/rules/unit-no-unknown/README.md): Disallow unknown units. -- [`unit-whitelist`](../../src/rules/unit-whitelist/README.md): Specify a whitelist of allowed units. +- [`unit-blacklist`](../../lib/rules/unit-blacklist/README.md): Specify a blacklist of disallowed units. +- [`unit-case`](../../lib/rules/unit-case/README.md): Specify lowercase or uppercase for units. +- [`unit-no-unknown`](../../lib/rules/unit-no-unknown/README.md): Disallow unknown units. +- [`unit-whitelist`](../../lib/rules/unit-whitelist/README.md): Specify a whitelist of allowed units. ### Value -- [`value-keyword-case`](../../src/rules/value-keyword-case/README.md): Specify lowercase or uppercase for keywords values. -- [`value-no-vendor-prefix`](../../src/rules/value-no-vendor-prefix/README.md): Disallow vendor prefixes for values. +- [`value-keyword-case`](../../lib/rules/value-keyword-case/README.md): Specify lowercase or uppercase for keywords values. +- [`value-no-vendor-prefix`](../../lib/rules/value-no-vendor-prefix/README.md): Disallow vendor prefixes for values. ### Value list -- [`value-list-comma-newline-after`](../../src/rules/value-list-comma-newline-after/README.md): Require a newline or disallow whitespace after the commas of value lists. -- [`value-list-comma-newline-before`](../../src/rules/value-list-comma-newline-before/README.md): Require a newline or disallow whitespace before the commas of value lists. -- [`value-list-comma-space-after`](../../src/rules/value-list-comma-space-after/README.md): Require a single space or disallow whitespace after the commas of value lists. -- [`value-list-comma-space-before`](../../src/rules/value-list-comma-space-before/README.md): Require a single space or disallow whitespace before the commas of value lists. -- [`value-list-max-empty-lines`](../../src/rules/value-list-max-empty-lines/README.md): Limit the number of adjacent empty lines within value lists. +- [`value-list-comma-newline-after`](../../lib/rules/value-list-comma-newline-after/README.md): Require a newline or disallow whitespace after the commas of value lists. +- [`value-list-comma-newline-before`](../../lib/rules/value-list-comma-newline-before/README.md): Require a newline or disallow whitespace before the commas of value lists. +- [`value-list-comma-space-after`](../../lib/rules/value-list-comma-space-after/README.md): Require a single space or disallow whitespace after the commas of value lists. +- [`value-list-comma-space-before`](../../lib/rules/value-list-comma-space-before/README.md): Require a single space or disallow whitespace before the commas of value lists. +- [`value-list-max-empty-lines`](../../lib/rules/value-list-max-empty-lines/README.md): Limit the number of adjacent empty lines within value lists. ### Custom property -- [`custom-property-empty-line-before`](../../src/rules/custom-property-empty-line-before/README.md): Require or disallow an empty line before custom properties. -- [`custom-property-no-outside-root`](../../src/rules/custom-property-no-outside-root/README.md): Disallow custom properties outside of `:root` rules. -- [`custom-property-pattern`](../../src/rules/custom-property-pattern/README.md): Specify a pattern for custom properties. +- [`custom-property-empty-line-before`](../../lib/rules/custom-property-empty-line-before/README.md): Require or disallow an empty line before custom properties. +- [`custom-property-no-outside-root`](../../lib/rules/custom-property-no-outside-root/README.md): Disallow custom properties outside of `:root` rules. +- [`custom-property-pattern`](../../lib/rules/custom-property-pattern/README.md): Specify a pattern for custom properties. ### Shorthand property -- [`shorthand-property-no-redundant-values`](../../src/rules/shorthand-property-no-redundant-values/README.md): Disallow redundant values in shorthand properties. +- [`shorthand-property-no-redundant-values`](../../lib/rules/shorthand-property-no-redundant-values/README.md): Disallow redundant values in shorthand properties. ### Property -- [`property-blacklist`](../../src/rules/property-blacklist/README.md): Specify a blacklist of disallowed properties. -- [`property-case`](../../src/rules/property-case/README.md): Specify lowercase or uppercase for properties. -- [`property-no-unknown`](../../src/rules/property-no-unknown/README.md): Disallow unknown properties. -- [`property-no-vendor-prefix`](../../src/rules/property-no-vendor-prefix/README.md): Disallow vendor prefixes for properties. -- [`property-whitelist`](../../src/rules/property-whitelist/README.md): Specify a whitelist of allowed properties. +- [`property-blacklist`](../../lib/rules/property-blacklist/README.md): Specify a blacklist of disallowed properties. +- [`property-case`](../../lib/rules/property-case/README.md): Specify lowercase or uppercase for properties. +- [`property-no-unknown`](../../lib/rules/property-no-unknown/README.md): Disallow unknown properties. +- [`property-no-vendor-prefix`](../../lib/rules/property-no-vendor-prefix/README.md): Disallow vendor prefixes for properties. +- [`property-whitelist`](../../lib/rules/property-whitelist/README.md): Specify a whitelist of allowed properties. ### Keyframe declaration -- [`keyframe-declaration-no-important`](../../src/rules/keyframe-declaration-no-important/README.md): Disallow `!important` within keyframe declarations. +- [`keyframe-declaration-no-important`](../../lib/rules/keyframe-declaration-no-important/README.md): Disallow `!important` within keyframe declarations. ### Declaration -- [`declaration-bang-space-after`](../../src/rules/declaration-bang-space-after/README.md): Require a single space or disallow whitespace after the bang of declarations. -- [`declaration-bang-space-before`](../../src/rules/declaration-bang-space-before/README.md): Require a single space or disallow whitespace before the bang of declarations. -- [`declaration-colon-newline-after`](../../src/rules/declaration-colon-newline-after/README.md): Require a newline or disallow whitespace after the colon of declarations. -- [`declaration-colon-space-after`](../../src/rules/declaration-colon-space-after/README.md): Require a single space or disallow whitespace after the colon of declarations. -- [`declaration-colon-space-before`](../../src/rules/declaration-colon-space-before/README.md): Require a single space or disallow whitespace before the colon of declarations. -- [`declaration-empty-line-before`](../../src/rules/declaration-empty-line-before/README.md): Require or disallow an empty line before declarations. -- [`declaration-no-important`](../../src/rules/declaration-no-important/README.md): Disallow `!important` within declarations. -- [`declaration-property-unit-blacklist`](../../src/rules/declaration-property-unit-blacklist/README.md): Specify a blacklist of disallowed property and unit pairs within declarations. -- [`declaration-property-unit-whitelist`](../../src/rules/declaration-property-unit-whitelist/README.md): Specify a whitelist of allowed property and unit pairs within declarations. -- [`declaration-property-value-blacklist`](../../src/rules/declaration-property-value-blacklist/README.md): Specify a blacklist of disallowed property and value pairs within declarations. -- [`declaration-property-value-whitelist`](../../src/rules/declaration-property-value-whitelist/README.md): Specify a whitelist of allowed property and value pairs within declarations. +- [`declaration-bang-space-after`](../../lib/rules/declaration-bang-space-after/README.md): Require a single space or disallow whitespace after the bang of declarations. +- [`declaration-bang-space-before`](../../lib/rules/declaration-bang-space-before/README.md): Require a single space or disallow whitespace before the bang of declarations. +- [`declaration-colon-newline-after`](../../lib/rules/declaration-colon-newline-after/README.md): Require a newline or disallow whitespace after the colon of declarations. +- [`declaration-colon-space-after`](../../lib/rules/declaration-colon-space-after/README.md): Require a single space or disallow whitespace after the colon of declarations. +- [`declaration-colon-space-before`](../../lib/rules/declaration-colon-space-before/README.md): Require a single space or disallow whitespace before the colon of declarations. +- [`declaration-empty-line-before`](../../lib/rules/declaration-empty-line-before/README.md): Require or disallow an empty line before declarations. +- [`declaration-no-important`](../../lib/rules/declaration-no-important/README.md): Disallow `!important` within declarations. +- [`declaration-property-unit-blacklist`](../../lib/rules/declaration-property-unit-blacklist/README.md): Specify a blacklist of disallowed property and unit pairs within declarations. +- [`declaration-property-unit-whitelist`](../../lib/rules/declaration-property-unit-whitelist/README.md): Specify a whitelist of allowed property and unit pairs within declarations. +- [`declaration-property-value-blacklist`](../../lib/rules/declaration-property-value-blacklist/README.md): Specify a blacklist of disallowed property and value pairs within declarations. +- [`declaration-property-value-whitelist`](../../lib/rules/declaration-property-value-whitelist/README.md): Specify a whitelist of allowed property and value pairs within declarations. ### Declaration block -- [`declaration-block-no-duplicate-properties`](../../src/rules/declaration-block-no-duplicate-properties/README.md): Disallow duplicate properties within declaration blocks. -- [`declaration-block-no-ignored-properties`](../../src/rules/declaration-block-no-ignored-properties/README.md): Disallow property values that are ignored due to another property value in the same rule. -- [`declaration-block-no-redundant-longhand-properties`](../../src/rules/declaration-block-no-redundant-longhand-properties/README.md): Disallow longhand properties that can be combined into one shorthand property. -- [`declaration-block-no-shorthand-property-overrides`](../../src/rules/declaration-block-no-shorthand-property-overrides/README.md): Disallow shorthand properties that override related longhand properties within declaration blocks. -- [`declaration-block-properties-order`](../../src/rules/declaration-block-properties-order/README.md): Specify the order of properties within declaration blocks. -- [`declaration-block-semicolon-newline-after`](../../src/rules/declaration-block-semicolon-newline-after/README.md): Require a newline or disallow whitespace after the semicolons of declaration blocks. -- [`declaration-block-semicolon-newline-before`](../../src/rules/declaration-block-semicolon-newline-before/README.md): Require a newline or disallow whitespace before the semicolons of declaration blocks. -- [`declaration-block-semicolon-space-after`](../../src/rules/declaration-block-semicolon-space-after/README.md): Require a single space or disallow whitespace after the semicolons of declaration blocks. -- [`declaration-block-semicolon-space-before`](../../src/rules/declaration-block-semicolon-space-before/README.md): Require a single space or disallow whitespace before the semicolons of declaration blocks. -- [`declaration-block-single-line-max-declarations`](../../src/rules/declaration-block-single-line-max-declarations/README.md): Limit the number of declaration within single line declaration blocks. -- [`declaration-block-trailing-semicolon`](../../src/rules/declaration-block-trailing-semicolon/README.md): Require or disallow a trailing semicolon within declaration blocks. +- [`declaration-block-no-duplicate-properties`](../../lib/rules/declaration-block-no-duplicate-properties/README.md): Disallow duplicate properties within declaration blocks. +- [`declaration-block-no-ignored-properties`](../../lib/rules/declaration-block-no-ignored-properties/README.md): Disallow property values that are ignored due to another property value in the same rule. +- [`declaration-block-no-redundant-longhand-properties`](../../lib/rules/declaration-block-no-redundant-longhand-properties/README.md): Disallow longhand properties that can be combined into one shorthand property. +- [`declaration-block-no-shorthand-property-overrides`](../../lib/rules/declaration-block-no-shorthand-property-overrides/README.md): Disallow shorthand properties that override related longhand properties within declaration blocks. +- [`declaration-block-properties-order`](../../lib/rules/declaration-block-properties-order/README.md): Specify the order of properties within declaration blocks. +- [`declaration-block-semicolon-newline-after`](../../lib/rules/declaration-block-semicolon-newline-after/README.md): Require a newline or disallow whitespace after the semicolons of declaration blocks. +- [`declaration-block-semicolon-newline-before`](../../lib/rules/declaration-block-semicolon-newline-before/README.md): Require a newline or disallow whitespace before the semicolons of declaration blocks. +- [`declaration-block-semicolon-space-after`](../../lib/rules/declaration-block-semicolon-space-after/README.md): Require a single space or disallow whitespace after the semicolons of declaration blocks. +- [`declaration-block-semicolon-space-before`](../../lib/rules/declaration-block-semicolon-space-before/README.md): Require a single space or disallow whitespace before the semicolons of declaration blocks. +- [`declaration-block-single-line-max-declarations`](../../lib/rules/declaration-block-single-line-max-declarations/README.md): Limit the number of declaration within single line declaration blocks. +- [`declaration-block-trailing-semicolon`](../../lib/rules/declaration-block-trailing-semicolon/README.md): Require or disallow a trailing semicolon within declaration blocks. ### Block -- [`block-closing-brace-empty-line-before`](../../src/rules/block-closing-brace-empty-line-before/README.md): Require or disallow an empty line before the closing brace of blocks. -- [`block-closing-brace-newline-after`](../../src/rules/block-closing-brace-newline-after/README.md): Require a newline or disallow whitespace after the closing brace of blocks. -- [`block-closing-brace-newline-before`](../../src/rules/block-closing-brace-newline-before/README.md): Require a newline or disallow whitespace before the closing brace of blocks. -- [`block-closing-brace-space-after`](../../src/rules/block-closing-brace-space-after/README.md): Require a single space or disallow whitespace after the closing brace of blocks. -- [`block-closing-brace-space-before`](../../src/rules/block-closing-brace-space-before/README.md): Require a single space or disallow whitespace before the closing brace of blocks. -- [`block-no-empty`](../../src/rules/block-no-empty/README.md): Disallow empty blocks. -- [`block-no-single-line`](../../src/rules/block-no-single-line/README.md): Disallow single-line blocks. -- [`block-opening-brace-newline-after`](../../src/rules/block-opening-brace-newline-after/README.md): Require a newline after the opening brace of blocks. -- [`block-opening-brace-newline-before`](../../src/rules/block-opening-brace-newline-before/README.md): Require a newline or disallow whitespace before the opening brace of blocks. -- [`block-opening-brace-space-after`](../../src/rules/block-opening-brace-space-after/README.md): Require a single space or disallow whitespace after the opening brace of blocks. -- [`block-opening-brace-space-before`](../../src/rules/block-opening-brace-space-before/README.md): Require a single space or disallow whitespace before the opening brace of blocks. +- [`block-closing-brace-empty-line-before`](../../lib/rules/block-closing-brace-empty-line-before/README.md): Require or disallow an empty line before the closing brace of blocks. +- [`block-closing-brace-newline-after`](../../lib/rules/block-closing-brace-newline-after/README.md): Require a newline or disallow whitespace after the closing brace of blocks. +- [`block-closing-brace-newline-before`](../../lib/rules/block-closing-brace-newline-before/README.md): Require a newline or disallow whitespace before the closing brace of blocks. +- [`block-closing-brace-space-after`](../../lib/rules/block-closing-brace-space-after/README.md): Require a single space or disallow whitespace after the closing brace of blocks. +- [`block-closing-brace-space-before`](../../lib/rules/block-closing-brace-space-before/README.md): Require a single space or disallow whitespace before the closing brace of blocks. +- [`block-no-empty`](../../lib/rules/block-no-empty/README.md): Disallow empty blocks. +- [`block-no-single-line`](../../lib/rules/block-no-single-line/README.md): Disallow single-line blocks. +- [`block-opening-brace-newline-after`](../../lib/rules/block-opening-brace-newline-after/README.md): Require a newline after the opening brace of blocks. +- [`block-opening-brace-newline-before`](../../lib/rules/block-opening-brace-newline-before/README.md): Require a newline or disallow whitespace before the opening brace of blocks. +- [`block-opening-brace-space-after`](../../lib/rules/block-opening-brace-space-after/README.md): Require a single space or disallow whitespace after the opening brace of blocks. +- [`block-opening-brace-space-before`](../../lib/rules/block-opening-brace-space-before/README.md): Require a single space or disallow whitespace before the opening brace of blocks. ### Selector -- [`selector-attribute-brackets-space-inside`](../../src/rules/selector-attribute-brackets-space-inside/README.md): Require a single space or disallow whitespace on the inside of the brackets within attribute selectors. -- [`selector-attribute-operator-blacklist`](../../src/rules/selector-attribute-operator-blacklist/README.md): Specify a blacklist of disallowed attribute operators. -- [`selector-attribute-operator-space-after`](../../src/rules/selector-attribute-operator-space-after/README.md): Require a single space or disallow whitespace after operators within attribute selectors. -- [`selector-attribute-operator-space-before`](../../src/rules/selector-attribute-operator-space-before/README.md): Require a single space or disallow whitespace before operators within attribute selectors. -- [`selector-attribute-operator-whitelist`](../../src/rules/selector-attribute-operator-whitelist/README.md): Specify a whitelist of allowed attribute operators. -- [`selector-attribute-quotes`](../../src/rules/selector-attribute-quotes/README.md): Require or disallow quotes for attribute values. -- [`selector-class-pattern`](../../src/rules/selector-class-pattern/README.md): Specify a pattern for class selectors. -- [`selector-combinator-space-after`](../../src/rules/selector-combinator-space-after/README.md): Require a single space or disallow whitespace after the combinators of selectors. -- [`selector-combinator-space-before`](../../src/rules/selector-combinator-space-before/README.md): Require a single space or disallow whitespace before the combinators of selectors. -- [`selector-descendant-combinator-no-non-space`](../../src/rules/selector-descendant-combinator-no-non-space/README.md): Disallow non-space characters for descendant combinators of selectors. -- [`selector-id-pattern`](../../src/rules/selector-id-pattern/README.md): Specify a pattern for id selectors. -- [`selector-max-compound-selectors`](../../src/rules/selector-max-compound-selectors/README.md): Limit the number of compound selectors in a selector. -- [`selector-max-specificity`](../../src/rules/selector-max-specificity/README.md): Limit the specificity of selectors. -- [`selector-nested-pattern`](../../src/rules/selector-nested-pattern/README.md): Specify a pattern for the selectors of rules nested within rules. -- [`selector-no-attribute`](../../src/rules/selector-no-attribute/README.md): Disallow attribute selectors. -- [`selector-no-combinator`](../../src/rules/selector-no-combinator/README.md): Disallow combinators in selectors. -- [`selector-no-empty`](../../src/rules/selector-no-empty/README.md): Disallow empty selectors. -- [`selector-no-id`](../../src/rules/selector-no-id/README.md): Disallow id selectors. -- [`selector-no-qualifying-type`](../../src/rules/selector-no-qualifying-type/README.md): Disallow qualifying a selector by type. -- [`selector-no-type`](../../src/rules/selector-no-type/README.md): Disallow type selectors. -- [`selector-no-universal`](../../src/rules/selector-no-universal/README.md): Disallow the universal selector. -- [`selector-no-vendor-prefix`](../../src/rules/selector-no-vendor-prefix/README.md): Disallow vendor prefixes for selectors. -- [`selector-pseudo-class-blacklist`](../../src/rules/selector-pseudo-class-blacklist/README.md): Specify a blacklist of disallowed pseudo-class selectors. -- [`selector-pseudo-class-case`](../../src/rules/selector-pseudo-class-case/README.md): Specify lowercase or uppercase for pseudo-class selectors. -- [`selector-pseudo-class-no-unknown`](../../src/rules/selector-pseudo-class-no-unknown/README.md): Disallow unknown pseudo-class selectors. -- [`selector-pseudo-class-parentheses-space-inside`](../../src/rules/selector-pseudo-class-parentheses-space-inside/README.md): Require a single space or disallow whitespace on the inside of the parentheses within pseudo-class selectors. -- [`selector-pseudo-class-whitelist`](../../src/rules/selector-pseudo-class-whitelist/README.md): Specify a whitelist of allowed pseudo-class selectors. -- [`selector-pseudo-element-case`](../../src/rules/selector-pseudo-element-case/README.md): Specify lowercase or uppercase for pseudo-element selectors. -- [`selector-pseudo-element-colon-notation`](../../src/rules/selector-pseudo-element-colon-notation/README.md): Specify single or double colon notation for applicable pseudo-elements. -- [`selector-pseudo-element-no-unknown`](../../src/rules/selector-pseudo-element-no-unknown/README.md): Disallow unknown pseudo-element selectors. -- [`selector-root-no-composition`](../../src/rules/selector-root-no-composition/README.md): Disallow the composition of `:root` in selectors. -- [`selector-type-case`](../../src/rules/selector-type-case/README.md): Specify lowercase or uppercase for type selector. -- [`selector-type-no-unknown`](../../src/rules/selector-type-no-unknown/README.md): Disallow unknown type selectors. -- [`selector-max-empty-lines`](../../src/rules/selector-max-empty-lines/README.md): Limit the number of adjacent empty lines within selectors. +- [`selector-attribute-brackets-space-inside`](../../lib/rules/selector-attribute-brackets-space-inside/README.md): Require a single space or disallow whitespace on the inside of the brackets within attribute selectors. +- [`selector-attribute-operator-blacklist`](../../lib/rules/selector-attribute-operator-blacklist/README.md): Specify a blacklist of disallowed attribute operators. +- [`selector-attribute-operator-space-after`](../../lib/rules/selector-attribute-operator-space-after/README.md): Require a single space or disallow whitespace after operators within attribute selectors. +- [`selector-attribute-operator-space-before`](../../lib/rules/selector-attribute-operator-space-before/README.md): Require a single space or disallow whitespace before operators within attribute selectors. +- [`selector-attribute-operator-whitelist`](../../lib/rules/selector-attribute-operator-whitelist/README.md): Specify a whitelist of allowed attribute operators. +- [`selector-attribute-quotes`](../../lib/rules/selector-attribute-quotes/README.md): Require or disallow quotes for attribute values. +- [`selector-class-pattern`](../../lib/rules/selector-class-pattern/README.md): Specify a pattern for class selectors. +- [`selector-combinator-space-after`](../../lib/rules/selector-combinator-space-after/README.md): Require a single space or disallow whitespace after the combinators of selectors. +- [`selector-combinator-space-before`](../../lib/rules/selector-combinator-space-before/README.md): Require a single space or disallow whitespace before the combinators of selectors. +- [`selector-descendant-combinator-no-non-space`](../../lib/rules/selector-descendant-combinator-no-non-space/README.md): Disallow non-space characters for descendant combinators of selectors. +- [`selector-id-pattern`](../../lib/rules/selector-id-pattern/README.md): Specify a pattern for id selectors. +- [`selector-max-compound-selectors`](../../lib/rules/selector-max-compound-selectors/README.md): Limit the number of compound selectors in a selector. +- [`selector-max-specificity`](../../lib/rules/selector-max-specificity/README.md): Limit the specificity of selectors. +- [`selector-nested-pattern`](../../lib/rules/selector-nested-pattern/README.md): Specify a pattern for the selectors of rules nested within rules. +- [`selector-no-attribute`](../../lib/rules/selector-no-attribute/README.md): Disallow attribute selectors. +- [`selector-no-combinator`](../../lib/rules/selector-no-combinator/README.md): Disallow combinators in selectors. +- [`selector-no-empty`](../../lib/rules/selector-no-empty/README.md): Disallow empty selectors. +- [`selector-no-id`](../../lib/rules/selector-no-id/README.md): Disallow id selectors. +- [`selector-no-qualifying-type`](../../lib/rules/selector-no-qualifying-type/README.md): Disallow qualifying a selector by type. +- [`selector-no-type`](../../lib/rules/selector-no-type/README.md): Disallow type selectors. +- [`selector-no-universal`](../../lib/rules/selector-no-universal/README.md): Disallow the universal selector. +- [`selector-no-vendor-prefix`](../../lib/rules/selector-no-vendor-prefix/README.md): Disallow vendor prefixes for selectors. +- [`selector-pseudo-class-blacklist`](../../lib/rules/selector-pseudo-class-blacklist/README.md): Specify a blacklist of disallowed pseudo-class selectors. +- [`selector-pseudo-class-case`](../../lib/rules/selector-pseudo-class-case/README.md): Specify lowercase or uppercase for pseudo-class selectors. +- [`selector-pseudo-class-no-unknown`](../../lib/rules/selector-pseudo-class-no-unknown/README.md): Disallow unknown pseudo-class selectors. +- [`selector-pseudo-class-parentheses-space-inside`](../../lib/rules/selector-pseudo-class-parentheses-space-inside/README.md): Require a single space or disallow whitespace on the inside of the parentheses within pseudo-class selectors. +- [`selector-pseudo-class-whitelist`](../../lib/rules/selector-pseudo-class-whitelist/README.md): Specify a whitelist of allowed pseudo-class selectors. +- [`selector-pseudo-element-case`](../../lib/rules/selector-pseudo-element-case/README.md): Specify lowercase or uppercase for pseudo-element selectors. +- [`selector-pseudo-element-colon-notation`](../../lib/rules/selector-pseudo-element-colon-notation/README.md): Specify single or double colon notation for applicable pseudo-elements. +- [`selector-pseudo-element-no-unknown`](../../lib/rules/selector-pseudo-element-no-unknown/README.md): Disallow unknown pseudo-element selectors. +- [`selector-root-no-composition`](../../lib/rules/selector-root-no-composition/README.md): Disallow the composition of `:root` in selectors. +- [`selector-type-case`](../../lib/rules/selector-type-case/README.md): Specify lowercase or uppercase for type selector. +- [`selector-type-no-unknown`](../../lib/rules/selector-type-no-unknown/README.md): Disallow unknown type selectors. +- [`selector-max-empty-lines`](../../lib/rules/selector-max-empty-lines/README.md): Limit the number of adjacent empty lines within selectors. ### Selector list -- [`selector-list-comma-newline-after`](../../src/rules/selector-list-comma-newline-after/README.md): Require a newline or disallow whitespace after the commas of selector lists. -- [`selector-list-comma-newline-before`](../../src/rules/selector-list-comma-newline-before/README.md): Require a newline or disallow whitespace before the commas of selector lists. -- [`selector-list-comma-space-after`](../../src/rules/selector-list-comma-space-after/README.md): Require a single space or disallow whitespace after the commas of selector lists. -- [`selector-list-comma-space-before`](../../src/rules/selector-list-comma-space-before/README.md): Require a single space or disallow whitespace before the commas of selector lists. +- [`selector-list-comma-newline-after`](../../lib/rules/selector-list-comma-newline-after/README.md): Require a newline or disallow whitespace after the commas of selector lists. +- [`selector-list-comma-newline-before`](../../lib/rules/selector-list-comma-newline-before/README.md): Require a newline or disallow whitespace before the commas of selector lists. +- [`selector-list-comma-space-after`](../../lib/rules/selector-list-comma-space-after/README.md): Require a single space or disallow whitespace after the commas of selector lists. +- [`selector-list-comma-space-before`](../../lib/rules/selector-list-comma-space-before/README.md): Require a single space or disallow whitespace before the commas of selector lists. ### Root rule -- [`root-no-standard-properties`](../../src/rules/root-no-standard-properties/README.md): Disallow standard properties inside `:root` rules. +- [`root-no-standard-properties`](../../lib/rules/root-no-standard-properties/README.md): Disallow standard properties inside `:root` rules. ### Rule -- [`rule-nested-empty-line-before`](../../src/rules/rule-nested-empty-line-before/README.md): Require or disallow an empty line before nested rules. -- [`rule-non-nested-empty-line-before`](../../src/rules/rule-non-nested-empty-line-before/README.md): Require or disallow an empty line before non-nested rules. +- [`rule-nested-empty-line-before`](../../lib/rules/rule-nested-empty-line-before/README.md): Require or disallow an empty line before nested rules. +- [`rule-non-nested-empty-line-before`](../../lib/rules/rule-non-nested-empty-line-before/README.md): Require or disallow an empty line before non-nested rules. ### Media feature -- [`media-feature-colon-space-after`](../../src/rules/media-feature-colon-space-after/README.md): Require a single space or disallow whitespace after the colon in media features. -- [`media-feature-colon-space-before`](../../src/rules/media-feature-colon-space-before/README.md): Require a single space or disallow whitespace before the colon in media features. -- [`media-feature-name-blacklist`](../../src/rules/media-feature-name-blacklist/README.md): Specify a blacklist of disallowed media feature names. -- [`media-feature-name-case`](../../src/rules/media-feature-name-case/README.md): Specify lowercase or uppercase for media feature names. -- [`media-feature-name-no-unknown`](../../src/rules/media-feature-name-no-unknown/README.md): Disallow unknown media feature names. -- [`media-feature-name-no-vendor-prefix`](../../src/rules/media-feature-name-no-vendor-prefix/README.md): Disallow vendor prefixes for media feature names. -- [`media-feature-name-whitelist`](../../src/rules/media-feature-name-whitelist/README.md): Specify a whitelist of allowed media feature names. -- [`media-feature-no-missing-punctuation`](../../src/rules/media-feature-no-missing-punctuation/README.md): Disallow missing punctuation for non-boolean media features. -- [`media-feature-parentheses-space-inside`](../../src/rules/media-feature-parentheses-space-inside/README.md): Require a single space or disallow whitespace on the inside of the parentheses within media features. -- [`media-feature-range-operator-space-after`](../../src/rules/media-feature-range-operator-space-after/README.md): Require a single space or disallow whitespace after the range operator in media features. -- [`media-feature-range-operator-space-before`](../../src/rules/media-feature-range-operator-space-before/README.md): Require a single space or disallow whitespace before the range operator in media features. +- [`media-feature-colon-space-after`](../../lib/rules/media-feature-colon-space-after/README.md): Require a single space or disallow whitespace after the colon in media features. +- [`media-feature-colon-space-before`](../../lib/rules/media-feature-colon-space-before/README.md): Require a single space or disallow whitespace before the colon in media features. +- [`media-feature-name-blacklist`](../../lib/rules/media-feature-name-blacklist/README.md): Specify a blacklist of disallowed media feature names. +- [`media-feature-name-case`](../../lib/rules/media-feature-name-case/README.md): Specify lowercase or uppercase for media feature names. +- [`media-feature-name-no-unknown`](../../lib/rules/media-feature-name-no-unknown/README.md): Disallow unknown media feature names. +- [`media-feature-name-no-vendor-prefix`](../../lib/rules/media-feature-name-no-vendor-prefix/README.md): Disallow vendor prefixes for media feature names. +- [`media-feature-name-whitelist`](../../lib/rules/media-feature-name-whitelist/README.md): Specify a whitelist of allowed media feature names. +- [`media-feature-no-missing-punctuation`](../../lib/rules/media-feature-no-missing-punctuation/README.md): Disallow missing punctuation for non-boolean media features. +- [`media-feature-parentheses-space-inside`](../../lib/rules/media-feature-parentheses-space-inside/README.md): Require a single space or disallow whitespace on the inside of the parentheses within media features. +- [`media-feature-range-operator-space-after`](../../lib/rules/media-feature-range-operator-space-after/README.md): Require a single space or disallow whitespace after the range operator in media features. +- [`media-feature-range-operator-space-before`](../../lib/rules/media-feature-range-operator-space-before/README.md): Require a single space or disallow whitespace before the range operator in media features. ### Custom media -- [`custom-media-pattern`](../../src/rules/custom-media-pattern/README.md): Specify a pattern for custom media query names. +- [`custom-media-pattern`](../../lib/rules/custom-media-pattern/README.md): Specify a pattern for custom media query names. ### Media query list -- [`media-query-list-comma-newline-after`](../../src/rules/media-query-list-comma-newline-after/README.md): Require a newline or disallow whitespace after the commas of media query lists. -- [`media-query-list-comma-newline-before`](../../src/rules/media-query-list-comma-newline-before/README.md): Require a newline or disallow whitespace before the commas of media query lists. -- [`media-query-list-comma-space-after`](../../src/rules/media-query-list-comma-space-after/README.md): Require a single space or disallow whitespace after the commas of media query lists. -- [`media-query-list-comma-space-before`](../../src/rules/media-query-list-comma-space-before/README.md): Require a single space or disallow whitespace before the commas of media query lists. +- [`media-query-list-comma-newline-after`](../../lib/rules/media-query-list-comma-newline-after/README.md): Require a newline or disallow whitespace after the commas of media query lists. +- [`media-query-list-comma-newline-before`](../../lib/rules/media-query-list-comma-newline-before/README.md): Require a newline or disallow whitespace before the commas of media query lists. +- [`media-query-list-comma-space-after`](../../lib/rules/media-query-list-comma-space-after/README.md): Require a single space or disallow whitespace after the commas of media query lists. +- [`media-query-list-comma-space-before`](../../lib/rules/media-query-list-comma-space-before/README.md): Require a single space or disallow whitespace before the commas of media query lists. ### At-rule -- [`at-rule-blacklist`](../../src/rules/at-rule-blacklist/README.md): Specify a blacklist of disallowed at-rules. -- [`at-rule-empty-line-before`](../../src/rules/at-rule-empty-line-before/README.md): Require or disallow an empty line before at-rules. -- [`at-rule-name-case`](../../src/rules/at-rule-name-case/README.md): Specify lowercase or uppercase for at-rules names. -- [`at-rule-name-newline-after`](../../src/rules/at-rule-name-newline-after/README.md): Require a newline after at-rule names. -- [`at-rule-name-space-after`](../../src/rules/at-rule-name-space-after/README.md): Require a single space after at-rule names. -- [`at-rule-no-unknown`](../../src/rules/at-rule-no-unknown/README.md): Disallow unknown at-rules. -- [`at-rule-no-vendor-prefix`](../../src/rules/at-rule-no-vendor-prefix/README.md): Disallow vendor prefixes for at-rules. -- [`at-rule-semicolon-newline-after`](../../src/rules/at-rule-semicolon-newline-after/README.md): Require a newline after the semicolon of at-rules. -- [`at-rule-whitelist`](../../src/rules/at-rule-whitelist/README.md): Specify a whitelist of allowed at-rules. +- [`at-rule-blacklist`](../../lib/rules/at-rule-blacklist/README.md): Specify a blacklist of disallowed at-rules. +- [`at-rule-empty-line-before`](../../lib/rules/at-rule-empty-line-before/README.md): Require or disallow an empty line before at-rules. +- [`at-rule-name-case`](../../lib/rules/at-rule-name-case/README.md): Specify lowercase or uppercase for at-rules names. +- [`at-rule-name-newline-after`](../../lib/rules/at-rule-name-newline-after/README.md): Require a newline after at-rule names. +- [`at-rule-name-space-after`](../../lib/rules/at-rule-name-space-after/README.md): Require a single space after at-rule names. +- [`at-rule-no-unknown`](../../lib/rules/at-rule-no-unknown/README.md): Disallow unknown at-rules. +- [`at-rule-no-vendor-prefix`](../../lib/rules/at-rule-no-vendor-prefix/README.md): Disallow vendor prefixes for at-rules. +- [`at-rule-semicolon-newline-after`](../../lib/rules/at-rule-semicolon-newline-after/README.md): Require a newline after the semicolon of at-rules. +- [`at-rule-whitelist`](../../lib/rules/at-rule-whitelist/README.md): Specify a whitelist of allowed at-rules. ### `stylelint-disable` comment -- [`stylelint-disable-reason`](../../src/rules/stylelint-disable-reason/README.md): Require a reason comment before or after `stylelint-disable` comments. +- [`stylelint-disable-reason`](../../lib/rules/stylelint-disable-reason/README.md): Require a reason comment before or after `stylelint-disable` comments. ### Comment -- [`comment-empty-line-before`](../../src/rules/comment-empty-line-before/README.md): Require or disallow an empty line before comments. -- [`comment-no-empty`](../../src/rules/comment-no-empty/README.md): Disallow empty comments. -- [`comment-whitespace-inside`](../../src/rules/comment-whitespace-inside/README.md): Require or disallow whitespace on the inside of comment markers. -- [`comment-word-blacklist`](../../src/rules/comment-word-blacklist/README.md): Specify a blacklist of disallowed words within comments. +- [`comment-empty-line-before`](../../lib/rules/comment-empty-line-before/README.md): Require or disallow an empty line before comments. +- [`comment-no-empty`](../../lib/rules/comment-no-empty/README.md): Disallow empty comments. +- [`comment-whitespace-inside`](../../lib/rules/comment-whitespace-inside/README.md): Require or disallow whitespace on the inside of comment markers. +- [`comment-word-blacklist`](../../lib/rules/comment-word-blacklist/README.md): Specify a blacklist of disallowed words within comments. ### General / Sheet -- [`indentation`](../../src/rules/indentation/README.md): Specify indentation. -- [`max-empty-lines`](../../src/rules/max-empty-lines/README.md): Limit the number of adjacent empty lines. -- [`max-line-length`](../../src/rules/max-line-length/README.md): Limit the length of a line. -- [`max-nesting-depth`](../../src/rules/max-nesting-depth/README.md): Limit the depth of nesting. -- [`no-browser-hacks`](../../src/rules/no-browser-hacks/README.md): Disallow browser hacks that are irrelevant to the browsers you are targeting. -- [`no-descending-specificity`](../../src/rules/no-descending-specificity/README.md): Disallow selectors of lower specificity from coming after overriding selectors of higher specificity. -- [`no-duplicate-selectors`](../../src/rules/no-duplicate-selectors/README.md): Disallow duplicate selectors. -- [`no-empty-source`](../../src/rules/no-empty-source/README.md): Disallow empty sources. -- [`no-eol-whitespace`](../../src/rules/no-eol-whitespace/README.md): Disallow end-of-line whitespace. -- [`no-extra-semicolons`](../../src/rules/no-extra-semicolons/README.md): Disallow extra semicolons. -- [`no-indistinguishable-colors`](../../src/rules/no-indistinguishable-colors/README.md): Disallow colors that are suspiciously close to being identical. -- [`no-invalid-double-slash-comments`](../../src/rules/no-invalid-double-slash-comments/README.md): Disallow double-slash comments (`//...`) which are not supported by CSS. -- [`no-missing-end-of-source-newline`](../../src/rules/no-missing-end-of-source-newline/README.md): Disallow missing end-of-source newlines. -- [`no-unknown-animations`](../../src/rules/no-unknown-animations/README.md): Disallow animation names that do not correspond to a `@keyframes` declaration. -- [`no-unsupported-browser-features`](../../src/rules/no-unsupported-browser-features/README.md): Disallow features that are unsupported by the browsers that you are targeting. +- [`indentation`](../../lib/rules/indentation/README.md): Specify indentation. +- [`max-empty-lines`](../../lib/rules/max-empty-lines/README.md): Limit the number of adjacent empty lines. +- [`max-line-length`](../../lib/rules/max-line-length/README.md): Limit the length of a line. +- [`max-nesting-depth`](../../lib/rules/max-nesting-depth/README.md): Limit the depth of nesting. +- [`no-browser-hacks`](../../lib/rules/no-browser-hacks/README.md): Disallow browser hacks that are irrelevant to the browsers you are targeting. +- [`no-descending-specificity`](../../lib/rules/no-descending-specificity/README.md): Disallow selectors of lower specificity from coming after overriding selectors of higher specificity. +- [`no-duplicate-selectors`](../../lib/rules/no-duplicate-selectors/README.md): Disallow duplicate selectors. +- [`no-empty-source`](../../lib/rules/no-empty-source/README.md): Disallow empty sources. +- [`no-eol-whitespace`](../../lib/rules/no-eol-whitespace/README.md): Disallow end-of-line whitespace. +- [`no-extra-semicolons`](../../lib/rules/no-extra-semicolons/README.md): Disallow extra semicolons. +- [`no-indistinguishable-colors`](../../lib/rules/no-indistinguishable-colors/README.md): Disallow colors that are suspiciously close to being identical. +- [`no-invalid-double-slash-comments`](../../lib/rules/no-invalid-double-slash-comments/README.md): Disallow double-slash comments (`//...`) which are not supported by CSS. +- [`no-missing-end-of-source-newline`](../../lib/rules/no-missing-end-of-source-newline/README.md): Disallow missing end-of-source newlines. +- [`no-unknown-animations`](../../lib/rules/no-unknown-animations/README.md): Disallow animation names that do not correspond to a `@keyframes` declaration. +- [`no-unsupported-browser-features`](../../lib/rules/no-unsupported-browser-features/README.md): Disallow features that are unsupported by the browsers that you are targeting. diff --git a/src/__tests__/createLinter.test.js b/lib/__tests__/createLinter.test.js similarity index 58% rename from src/__tests__/createLinter.test.js rename to lib/__tests__/createLinter.test.js index 2c3c797ca2..c5db1a444a 100644 --- a/src/__tests__/createLinter.test.js +++ b/lib/__tests__/createLinter.test.js @@ -1,11 +1,13 @@ -import path from "path" -import pluginWarnAboutFoo from "./fixtures/plugin-warn-about-foo" -import stylelint from ".." +"use strict" + +const path = require("path") +const pluginWarnAboutFoo = require("./fixtures/plugin-warn-about-foo") +const stylelint = require("..") it("createLinter().getConfigForFile augmented config loads", () => { const linter = stylelint.createLinter() const filepath = path.join(__dirname, "fixtures/getConfigForFile/a/b/foo.css") - return linter.getConfigForFile(filepath).then((result) => { + return linter.getConfigForFile(filepath).then(result => { expect(result).toEqual({ config: { plugins: [path.join(__dirname, "/fixtures/plugin-warn-about-foo.js")], @@ -24,25 +26,12 @@ it("createLinter().getConfigForFile augmented config loads", () => { it("createLinter().isPathIgnored", () => { const config = { - ignoreFiles: [ - "**/*.css", - "!**/invalid-hex.css", - ], + ignoreFiles: [ "**/*.css", "!**/invalid-hex.css" ], rules: { "block-no-empty": true }, } const linter = stylelint.createLinter({ config }) - return Promise.all([ - linter.isPathIgnored("a.css"), - linter.isPathIgnored("foo/bar/baz.css"), - linter.isPathIgnored("foo/bar/baz.scss"), - linter.isPathIgnored("foo/invalid-hex.css"), - ]).then((results) => { - expect(results).toEqual([ - true, - true, - false, - false, - ]) + return Promise.all([ linter.isPathIgnored("a.css"), linter.isPathIgnored("foo/bar/baz.css"), linter.isPathIgnored("foo/bar/baz.scss"), linter.isPathIgnored("foo/invalid-hex.css") ]).then(results => { + expect(results).toEqual([ true, true, false, false ]) }) }) diff --git a/src/__tests__/defaultSeverity.test.js b/lib/__tests__/defaultSeverity.test.js similarity index 86% rename from src/__tests__/defaultSeverity.test.js rename to lib/__tests__/defaultSeverity.test.js index 6b7cd5fa2f..dbb730ef35 100644 --- a/src/__tests__/defaultSeverity.test.js +++ b/lib/__tests__/defaultSeverity.test.js @@ -1,4 +1,6 @@ -import postcssPlugin from "../postcssPlugin" +"use strict" + +const postcssPlugin = require("../postcssPlugin") it("`defaultSeverity` option set to warning", () => { const config = { diff --git a/src/__tests__/disableRanges-integration-test.js b/lib/__tests__/disableRanges-integration-test.js similarity index 70% rename from src/__tests__/disableRanges-integration-test.js rename to lib/__tests__/disableRanges-integration-test.js index 20653b6ef1..c59814b678 100644 --- a/src/__tests__/disableRanges-integration-test.js +++ b/lib/__tests__/disableRanges-integration-test.js @@ -1,24 +1,14 @@ -import blockNoEmpty, { - messages as blockNoEmptyMessages, - ruleName as blockNoEmptyName, -} from "../rules/block-no-empty" -import maxLineLength, { - messages as maxLineLengthMessages, - ruleName as maxLineLengthName, -} from "../rules/max-line-length" -import selectorCombinatorSpaceBefore, { - messages as selectorCombinatorSpaceBeforeMessages, - ruleName as selectorCombinatorSpaceBeforeName, -} from "../rules/selector-combinator-space-before" -import stringQuotes, { - messages as stringQuotesMessages, - ruleName as stringQuotesName, -} from "../rules/string-quotes" -import testRule from "../testUtils/testRule.js" +"use strict" + +const blockNoEmpty = require("../rules/block-no-empty") +const maxLineLength = require("../rules/max-line-length") +const selectorCombinatorSpaceBefore = require("../rules/selector-combinator-space-before") +const stringQuotes = require("../rules/string-quotes") +const testRule = require("../testUtils/testRule.js") // disabling all rules testRule(blockNoEmpty, { - ruleName: blockNoEmptyName, + ruleName: blockNoEmpty.ruleName, config: [undefined], skipBasicChecks: true, @@ -36,70 +26,70 @@ testRule(blockNoEmpty, { reject: [ { code: "a {}", - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, }, { code: "a {}\n/* stylelint-disable */", - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, }, { code: "a {}\n/* stylelint-disable-line */", - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, }, { code: "/* stylelint-disable-line */\na {}", - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, }, { code: "/* stylelint-disable-next-line */\na{}\nb {}", - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, }, { code: "/* stylelint-disable-next-line */ a{}", - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, }, { code: "a {}\n/* stylelint-disable-next-line */", - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, } ], }) testRule(selectorCombinatorSpaceBefore, { - ruleName: selectorCombinatorSpaceBeforeName, + ruleName: selectorCombinatorSpaceBefore.ruleName, config: ["always"], skipBasicChecks: true, reject: [{ code: "a> b {}", - message: selectorCombinatorSpaceBeforeMessages.expectedBefore(">"), + message: selectorCombinatorSpaceBefore.messages.expectedBefore(">"), }], }) testRule(blockNoEmpty, { - ruleName: blockNoEmptyName, + ruleName: blockNoEmpty.ruleName, config: [undefined], skipBasicChecks: true, accept: [ { - code: `/* stylelint-disable ${blockNoEmptyName} */\na {}`, + code: `/* stylelint-disable ${blockNoEmpty.ruleName} */\na {}`, }, { - code: `/* stylelint-disable-line ${blockNoEmptyName} */ a {}`, + code: `/* stylelint-disable-line ${blockNoEmpty.ruleName} */ a {}`, }, { - code: `a {} /* stylelint-disable-line ${blockNoEmptyName} */`, + code: `a {} /* stylelint-disable-line ${blockNoEmpty.ruleName} */`, }, { - code: `/* stylelint-disable-next-line ${blockNoEmptyName} */\na {}`, + code: `/* stylelint-disable-next-line ${blockNoEmpty.ruleName} */\na {}`, } ], reject: [ { code: "/* stylelint-disable declaration-no-important */\na {}", - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, }, { code: "/* stylelint-disable-line declaration-no-important */\na {}", - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, }, { - code: `/* stylelint-disable-line ${blockNoEmptyName} */ a {}\nb {}`, - message: blockNoEmptyMessages.rejected, + code: `/* stylelint-disable-line ${blockNoEmpty.ruleName} */ a {}\nb {}`, + message: blockNoEmpty.messages.rejected, line: 2, column: 3, } ], }) testRule(selectorCombinatorSpaceBefore, { - ruleName: selectorCombinatorSpaceBeforeName, + ruleName: selectorCombinatorSpaceBefore.ruleName, config: ["always"], skipBasicChecks: true, @@ -113,15 +103,15 @@ testRule(selectorCombinatorSpaceBefore, { reject: [ { code: "/* stylelint-disable declaration-no-important */ a> b {}", - message: selectorCombinatorSpaceBeforeMessages.expectedBefore(">"), + message: selectorCombinatorSpaceBefore.messages.expectedBefore(">"), }, { code: "/* stylelint-disable-line declaration-no-important */\na> b {}", - message: selectorCombinatorSpaceBeforeMessages.expectedBefore(">"), + message: selectorCombinatorSpaceBefore.messages.expectedBefore(">"), } ], }) testRule(blockNoEmpty, { - ruleName: blockNoEmptyName, + ruleName: blockNoEmpty.ruleName, config: [undefined], skipBasicChecks: true, @@ -143,12 +133,12 @@ testRule(blockNoEmpty, { a {} `, - message: blockNoEmptyMessages.rejected, + message: blockNoEmpty.messages.rejected, }], }) testRule(maxLineLength, { - ruleName: maxLineLengthName, + ruleName: maxLineLength.ruleName, config: [80], skipBasicChecks: true, @@ -186,21 +176,21 @@ testRule(maxLineLength, { /* stylelint-enable block-no-empty */ `, - message: maxLineLengthMessages.expected(80), + message: maxLineLength.messages.expected(80), }, { code: ` /* stylelint-disable-line */ .abracadabracadabra { background: linear-gradient(to top, rgba(255, 255, 255, 0.1), rgba (255, 255, 255, 1)); } `, - message: maxLineLengthMessages.expected(80), + message: maxLineLength.messages.expected(80), }, { code: ` .abracadabracadabra { background: linear-gradient(to top, rgba(255, 255, 255, 0.1), rgba (255, 255, 255, 1)); } /* stylelint-disable-line */ `, - message: maxLineLengthMessages.expected(80), + message: maxLineLength.messages.expected(80), }, { code: ` /* stylelint-disable max-line-length */ @@ -209,7 +199,7 @@ testRule(maxLineLength, { .abracadabracadabra { background: linear-gradient(to top, rgba(255, 255, 255, 0.1), rgba (255, 255, 255, 1)); } `, - message: maxLineLengthMessages.expected(80), + message: maxLineLength.messages.expected(80), }, { code: ` /* stylelint-disable-next-line max-line-length */ @@ -217,12 +207,12 @@ testRule(maxLineLength, { .abracadabracadabra { background: linear-gradient(to top, rgba(255, 255, 255, 0.1), rgba (255, 255, 255, 1)); } `, - message: maxLineLengthMessages.expected(80), + message: maxLineLength.messages.expected(80), } ], }) testRule(stringQuotes, { - ruleName: stringQuotesName, + ruleName: stringQuotes.ruleName, config: ["single"], skipBasicChecks: true, @@ -247,7 +237,7 @@ testRule(stringQuotes, { /* stylelint-enable block-no-empty */ `, - message: stringQuotesMessages.expected("single"), + message: stringQuotes.messages.expected("single"), }, { code: ` /* stylelint-disable string-quotes */ @@ -256,6 +246,6 @@ testRule(stringQuotes, { .foo { content: "horse"; } `, - message: stringQuotesMessages.expected("single"), + message: stringQuotes.messages.expected("single"), } ], }) diff --git a/src/__tests__/disableRanges.test.js b/lib/__tests__/disableRanges.test.js similarity index 61% rename from src/__tests__/disableRanges.test.js rename to lib/__tests__/disableRanges.test.js index 88e6163ba0..cf01a0328f 100644 --- a/src/__tests__/disableRanges.test.js +++ b/lib/__tests__/disableRanges.test.js @@ -1,7 +1,9 @@ -import assignDisabledRanges from "../assignDisabledRanges" -import less from "postcss-less" -import postcss from "postcss" -import scss from "postcss-scss" +"use strict" + +const assignDisabledRanges = require("../assignDisabledRanges") +const less = require("postcss-less") +const postcss = require("postcss") +const scss = require("postcss-scss") it("no disabling", () => { return testDisableRanges("a {}").then(result => { @@ -20,24 +22,19 @@ it("disable without re-enabling", () => { }) it("disable and re-enable", () => { - return testDisableRanges( - `a {} + return testDisableRanges(`a {} /* stylelint-disable */ b {} /* stylelint-enable */ - .foo {}` - ).then(result => { - expect(result.stylelint.disabledRanges).toEqual({ - all: [ - { start: 2, end: 4 }, - ], + .foo {}`).then(result => { + expect(result.stylelint.disabledRanges).toEqual({ + all: [{ start: 2, end: 4 }], + }) }) - }) }) it("disable and re-enable twice", () => { - return testDisableRanges( - `a {} + return testDisableRanges(`a {} /* stylelint-disable */ b {} /* stylelint-enable */ @@ -45,15 +42,11 @@ it("disable and re-enable twice", () => { /* stylelint-disable */ b {} /* stylelint-enable */ - .foo {}` - ).then(result => { - expect(result.stylelint.disabledRanges).toEqual({ - all: [ - { start: 2, end: 4 }, - { start: 6, end: 8 }, - ], + .foo {}`).then(result => { + expect(result.stylelint.disabledRanges).toEqual({ + all: [ { start: 2, end: 4 }, { start: 6, end: 8 } ], + }) }) - }) }) it("disable rule without re-enabling", () => { @@ -63,10 +56,7 @@ it("disable rule without re-enabling", () => { "foo-bar": [{ start: 1 }], }) }).then(() => { - return testDisableRanges( - "/* stylelint-disable selector-combinator-space-before */\n" + - "a {}" - ).then(result => { + return testDisableRanges("/* stylelint-disable selector-combinator-space-before */\n" + "a {}").then(result => { expect(result.stylelint.disabledRanges).toEqual({ all: [], "selector-combinator-space-before": [{ start: 1 }], @@ -76,8 +66,7 @@ it("disable rule without re-enabling", () => { }) it("mixed disabling of specific and all rules, enabling of all", () => { - return testDisableRanges( - `a {} + return testDisableRanges(`a {} /* stylelint-disable foo-bar */ b {} /* stylelint-enable */ @@ -85,26 +74,17 @@ it("mixed disabling of specific and all rules, enabling of all", () => { /* stylelint-disable foo-bar,baz-maz */ b {} /* stylelint-enable */ - .foo {}` - ).then(result => { - expect(result.stylelint.disabledRanges).toEqual({ - all: [], - "foo-bar": [ - { start: 2, end: 4 }, - { start: 6, end: 8 }, - ], - "baz-maz": [ - { start: 6, end: 8 }, - ], + .foo {}`).then(result => { + expect(result.stylelint.disabledRanges).toEqual({ + all: [], + "foo-bar": [ { start: 2, end: 4 }, { start: 6, end: 8 } ], + "baz-maz": [{ start: 6, end: 8 }], + }) }) - }) }) it("disable rules with newline in rule list", () => { - return testDisableRanges( - "/* stylelint-disable foo-bar, hoo-hah,\n\tslime */\n" + - "b {}\n" - ).then(result => { + return testDisableRanges("/* stylelint-disable foo-bar, hoo-hah,\n\tslime */\n" + "b {}\n").then(result => { expect(result.stylelint.disabledRanges).toEqual({ all: [], "foo-bar": [{ start: 1 }], @@ -227,176 +207,146 @@ it("Less // line-disabling comment", () => { }) it("nested ranges all rule-specific", () => { - return testDisableRanges( - `/* stylelint-disable foo */ + return testDisableRanges(`/* stylelint-disable foo */ /* stylelint-disable bar */ /* stylelint-disable baz, hop */ /* stylelint-enable bar */ /* stylelint-enable foo, hop */ - /* stylelint-enable baz */` - ).then(result => { - expect(result.stylelint.disabledRanges).toEqual({ - all: [], - foo: [{ start: 1, end: 5 }], - bar: [{ start: 2, end: 4 }], - baz: [{ start: 3, end: 6 }], - hop: [{ start: 3, end: 5 }], + /* stylelint-enable baz */`).then(result => { + expect(result.stylelint.disabledRanges).toEqual({ + all: [], + foo: [{ start: 1, end: 5 }], + bar: [{ start: 2, end: 4 }], + baz: [{ start: 3, end: 6 }], + hop: [{ start: 3, end: 5 }], + }) }) - }) }) it("nested ranges all for all rules", () => { - return testDisableRanges( - `/* stylelint-disable */ + return testDisableRanges(`/* stylelint-disable */ /* stylelint-enable bar */ /* stylelint-disable bar */ - /* stylelint-enable */` - ).then(result => { - expect(result.stylelint.disabledRanges).toEqual({ - all: [{ start: 1, end: 4 }], - bar: [ - { start: 1, end: 2 }, - { start: 3, end: 4 }, - ], + /* stylelint-enable */`).then(result => { + expect(result.stylelint.disabledRanges).toEqual({ + all: [{ start: 1, end: 4 }], + bar: [ { start: 1, end: 2 }, { start: 3, end: 4 } ], + }) }) - }) }) it("nested ranges disable rules enable all", () => { - return testDisableRanges( - `/* stylelint-disable foo */ + return testDisableRanges(`/* stylelint-disable foo */ /* stylelint-disable bar, baz */ - /* stylelint-enable */` - ).then(result => { - expect(result.stylelint.disabledRanges).toEqual({ - all: [], - foo: [{ start: 1, end: 3 }], - bar: [{ start: 2, end: 3 }], - baz: [{ start: 2, end: 3 }], + /* stylelint-enable */`).then(result => { + expect(result.stylelint.disabledRanges).toEqual({ + all: [], + foo: [{ start: 1, end: 3 }], + bar: [{ start: 2, end: 3 }], + baz: [{ start: 2, end: 3 }], + }) }) - }) }) it("nested ranges mix disabling enabling all rules and specific rules", () => { - return testDisableRanges( - `/* stylelint-disable */ + return testDisableRanges(`/* stylelint-disable */ /* stylelint-enable foo */ /* stylelint-enable */ - /* stylelint-disable bar */` - ).then(result => { - expect(result.stylelint.disabledRanges).toEqual({ - all: [{ start: 1, end: 3 }], - foo: [{ start: 1, end: 2 }], - bar: [ - { start: 1, end: 3 }, - { start: 4 }, - ], + /* stylelint-disable bar */`).then(result => { + expect(result.stylelint.disabledRanges).toEqual({ + all: [{ start: 1, end: 3 }], + foo: [{ start: 1, end: 2 }], + bar: [ { start: 1, end: 3 }, { start: 4 } ], + }) }) - }) }) it("nested ranges another mix", () => { - return testDisableRanges( - `/* stylelint-disable */ + return testDisableRanges(`/* stylelint-disable */ /* stylelint-enable bar */ /* stylelint-enable foo */ /* stylelint-disable foo */ - /* stylelint-enable */` - ).then(result => { - expect(result.stylelint.disabledRanges).toEqual({ - all: [{ start: 1, end: 5 }], - foo: [ - { start: 1, end: 3 }, - { start: 4, end: 5 }, - ], - bar: [{ start: 1, end: 2 }], + /* stylelint-enable */`).then(result => { + expect(result.stylelint.disabledRanges).toEqual({ + all: [{ start: 1, end: 5 }], + foo: [ { start: 1, end: 3 }, { start: 4, end: 5 } ], + bar: [{ start: 1, end: 2 }], + }) }) - }) }) it("disable line for all rules after disabling all", () => { - return testDisableRanges( - `/* stylelint-disable */ - a {} /* stylelint-disable-line */`, - () => { throw new Error("should have errored") } - ).catch(err => { + return testDisableRanges(`/* stylelint-disable */ + a {} /* stylelint-disable-line */`, () => { + throw new Error("should have errored") + }).catch(err => { expect(err.reason).toBe("All rules have already been disabled") }) }) it("disable line for one rule after disabling all", () => { - return testDisableRanges( - `/* stylelint-disable */ - a {} /* stylelint-disable-line foo */`, - () => { throw new Error("should have errored") } - ).catch(err => { + return testDisableRanges(`/* stylelint-disable */ + a {} /* stylelint-disable-line foo */`, () => { + throw new Error("should have errored") + }).catch(err => { expect(err.reason).toBe("All rules have already been disabled") }) }) it("disable line for rule after disabling rule", () => { - return testDisableRanges( - `/* stylelint-disable foo */ - a {} /* stylelint-disable-line foo */`, - () => { throw new Error("should have errored") } - ).catch(err => { + return testDisableRanges(`/* stylelint-disable foo */ + a {} /* stylelint-disable-line foo */`, () => { + throw new Error("should have errored") + }).catch(err => { expect(err.reason).toBe("\"foo\" has already been disabled") }) }) it("disable all twice on the same line", () => { - return testDisableRanges( - "/* stylelint-disable */ /* stylelint-disable */", - () => { throw new Error("should have errored") } - ).catch(err => { + return testDisableRanges("/* stylelint-disable */ /* stylelint-disable */", () => { + throw new Error("should have errored") + }).catch(err => { expect(err.reason).toBe("All rules have already been disabled") }) }) it("disable rule twice on the same line", () => { - return testDisableRanges( - "/* stylelint-disable foo */ /* stylelint-disable foo*/", - () => { throw new Error("should have errored") } - ).catch(err => { + return testDisableRanges("/* stylelint-disable foo */ /* stylelint-disable foo*/", () => { + throw new Error("should have errored") + }).catch(err => { expect(err.reason).toBe("\"foo\" has already been disabled") }) }) it("enable all without disabling any", () => { - return testDisableRanges( - "/* stylelint-enable */", - () => { throw new Error("should have errored") } - ).catch(err => { + return testDisableRanges("/* stylelint-enable */", () => { + throw new Error("should have errored") + }).catch(err => { expect(err.reason).toBe("No rules have been disabled") }) }) it("enable rule without disabling any", () => { - return testDisableRanges( - "/* stylelint-enable foo */", - () => { throw new Error("should have errored") } - ).catch(err => { + return testDisableRanges("/* stylelint-enable foo */", () => { + throw new Error("should have errored") + }).catch(err => { expect(err.reason).toBe("\"foo\" has not been disabled") }) }) it("enable rule without disabling rule", () => { - return testDisableRanges( - `/* stylelint-disable */ + return testDisableRanges(`/* stylelint-disable */ /* stylelint-enable bar */ /* stylelint-enable foo */ /* stylelint-disable foo */ /* stylelint-enable bar */ - /* stylelint-enable */`, - () => { throw new Error("should have errored") } - ).catch(err => { + /* stylelint-enable */`, () => { + throw new Error("should have errored") + }).catch(err => { expect(err.reason).toBe("\"bar\" has not been disabled") }) }) function testDisableRanges(source, cb) { - return postcss() - .use(assignDisabledRanges) - .process(source) - .then(cb) + return postcss().use(assignDisabledRanges).process(source).then(cb) } diff --git a/src/__tests__/extends.test.js b/lib/__tests__/extends.test.js similarity index 55% rename from src/__tests__/extends.test.js rename to lib/__tests__/extends.test.js index d2e8daf9fa..c617567e8b 100644 --- a/src/__tests__/extends.test.js +++ b/lib/__tests__/extends.test.js @@ -1,8 +1,10 @@ -import configExtendingAnotherExtend from "./fixtures/config-extending-another-extend.json" -import configExtendingOne from "./fixtures/config-extending-one" -import configExtendingThreeWithOverride from "./fixtures/config-extending-three-with-override" -import path from "path" -import standalone from "../standalone" +"use strict" + +const configExtendingAnotherExtend = require("./fixtures/config-extending-another-extend.json") +const configExtendingOne = require("./fixtures/config-extending-one") +const configExtendingThreeWithOverride = require("./fixtures/config-extending-three-with-override") +const path = require("path") +const standalone = require("../standalone") const fixturesPath = path.join(__dirname, "fixtures") @@ -11,11 +13,11 @@ it("basic extending", () => { code: "a {}", config: configExtendingOne, configBasedir: path.join(__dirname, "fixtures"), - }).then(({ output, results }) => { - expect(typeof output, "string") - expect(results.length, 1) - expect(results[0].warnings.length, 1) - expect(results[0].warnings[0].rule, "block-no-empty") + }).then((linted) => { + expect(typeof linted.output).toBe("string") + expect(linted.results.length).toBe(1) + expect(linted.results[0].warnings.length).toBe(1) + expect(linted.results[0].warnings[0].rule).toBe("block-no-empty") }) }) @@ -24,11 +26,11 @@ it("recursive extending", () => { code: "a {}", config: configExtendingAnotherExtend, configBasedir: path.join(__dirname, "fixtures"), - }).then(({ output, results }) => { - expect(typeof output).toBe("string") - expect(results.length).toBe(1) - expect(results[0].warnings.length).toBe(1) - expect(results[0].warnings[0].rule).toBe("block-no-empty") + }).then((linted) => { + expect(typeof linted.output).toBe("string") + expect(linted.results.length).toBe(1) + expect(linted.results[0].warnings.length).toBe(1) + expect(linted.results[0].warnings[0].rule).toBe("block-no-empty") }) }) @@ -37,8 +39,8 @@ it("extending with overrides", () => { code: "a {}", config: configExtendingThreeWithOverride, configBasedir: path.join(__dirname, "fixtures"), - }).then(({ results }) => { - expect(results[0].warnings.length).toBe(0) + }).then((linted) => { + expect(linted.results[0].warnings.length).toBe(0) }) }) @@ -57,13 +59,11 @@ it("extending a config that is overridden", () => { return standalone({ code: "a { b: \"c\" }", config: { - extends: [ - `${fixturesPath}/config-string-quotes-single`, - ], + extends: [`${fixturesPath}/config-string-quotes-single`], rules: { "string-quotes": "double" }, }, - }).then(({ results }) => { - expect(results[0].warnings.length).toBe(0) + }).then((linted) => { + expect(linted.results[0].warnings.length).toBe(0) }) }) @@ -85,8 +85,8 @@ describe("extending a config from process.cwd", () => { config: { extends: ["./fixtures/config-string-quotes-single"], }, - }).then(({ results }) => { - expect(results[0].warnings.length).toBe(1) + }).then((linted) => { + expect(linted.results[0].warnings.length).toBe(1) }) }) }) diff --git a/src/__tests__/fixtures/config-at-rule-empty-line-before.json b/lib/__tests__/fixtures/config-at-rule-empty-line-before.json similarity index 100% rename from src/__tests__/fixtures/config-at-rule-empty-line-before.json rename to lib/__tests__/fixtures/config-at-rule-empty-line-before.json diff --git a/src/__tests__/fixtures/config-block-empty-ok.json b/lib/__tests__/fixtures/config-block-empty-ok.json similarity index 100% rename from src/__tests__/fixtures/config-block-empty-ok.json rename to lib/__tests__/fixtures/config-block-empty-ok.json diff --git a/src/__tests__/fixtures/config-block-no-empty.json b/lib/__tests__/fixtures/config-block-no-empty.json similarity index 100% rename from src/__tests__/fixtures/config-block-no-empty.json rename to lib/__tests__/fixtures/config-block-no-empty.json diff --git a/src/__tests__/fixtures/config-color-named-custom-message.yaml b/lib/__tests__/fixtures/config-color-named-custom-message.yaml similarity index 100% rename from src/__tests__/fixtures/config-color-named-custom-message.yaml rename to lib/__tests__/fixtures/config-color-named-custom-message.yaml diff --git a/src/__tests__/fixtures/config-color-no-invalid-hex.json b/lib/__tests__/fixtures/config-color-no-invalid-hex.json similarity index 100% rename from src/__tests__/fixtures/config-color-no-invalid-hex.json rename to lib/__tests__/fixtures/config-color-no-invalid-hex.json diff --git a/src/__tests__/fixtures/config-extending-and-ignoring.json b/lib/__tests__/fixtures/config-extending-and-ignoring.json similarity index 100% rename from src/__tests__/fixtures/config-extending-and-ignoring.json rename to lib/__tests__/fixtures/config-extending-and-ignoring.json diff --git a/src/__tests__/fixtures/config-extending-another-extend.json b/lib/__tests__/fixtures/config-extending-another-extend.json similarity index 100% rename from src/__tests__/fixtures/config-extending-another-extend.json rename to lib/__tests__/fixtures/config-extending-another-extend.json diff --git a/src/__tests__/fixtures/config-extending-one.json b/lib/__tests__/fixtures/config-extending-one.json similarity index 100% rename from src/__tests__/fixtures/config-extending-one.json rename to lib/__tests__/fixtures/config-extending-one.json diff --git a/src/__tests__/fixtures/config-extending-three-with-override.json b/lib/__tests__/fixtures/config-extending-three-with-override.json similarity index 100% rename from src/__tests__/fixtures/config-extending-three-with-override.json rename to lib/__tests__/fixtures/config-extending-three-with-override.json diff --git a/src/__tests__/fixtures/config-extending-two.json b/lib/__tests__/fixtures/config-extending-two.json similarity index 100% rename from src/__tests__/fixtures/config-extending-two.json rename to lib/__tests__/fixtures/config-extending-two.json diff --git a/src/__tests__/fixtures/config-extending-with-plugin.json b/lib/__tests__/fixtures/config-extending-with-plugin.json similarity index 100% rename from src/__tests__/fixtures/config-extending-with-plugin.json rename to lib/__tests__/fixtures/config-extending-with-plugin.json diff --git a/src/__tests__/fixtures/config-no-pixels.json b/lib/__tests__/fixtures/config-no-pixels.json similarity index 100% rename from src/__tests__/fixtures/config-no-pixels.json rename to lib/__tests__/fixtures/config-no-pixels.json diff --git a/src/__tests__/fixtures/config-per-file/a/.stylelintrc b/lib/__tests__/fixtures/config-per-file/a/.stylelintrc similarity index 100% rename from src/__tests__/fixtures/config-per-file/a/.stylelintrc rename to lib/__tests__/fixtures/config-per-file/a/.stylelintrc diff --git a/src/__tests__/fixtures/config-per-file/a/a.css b/lib/__tests__/fixtures/config-per-file/a/a.css similarity index 100% rename from src/__tests__/fixtures/config-per-file/a/a.css rename to lib/__tests__/fixtures/config-per-file/a/a.css diff --git a/src/__tests__/fixtures/config-per-file/a/b/.stylelintrc b/lib/__tests__/fixtures/config-per-file/a/b/.stylelintrc similarity index 100% rename from src/__tests__/fixtures/config-per-file/a/b/.stylelintrc rename to lib/__tests__/fixtures/config-per-file/a/b/.stylelintrc diff --git a/src/__tests__/fixtures/config-per-file/a/b/b.css b/lib/__tests__/fixtures/config-per-file/a/b/b.css similarity index 100% rename from src/__tests__/fixtures/config-per-file/a/b/b.css rename to lib/__tests__/fixtures/config-per-file/a/b/b.css diff --git a/src/__tests__/fixtures/config-per-file/a/b/c/.stylelintrc b/lib/__tests__/fixtures/config-per-file/a/b/c/.stylelintrc similarity index 100% rename from src/__tests__/fixtures/config-per-file/a/b/c/.stylelintrc rename to lib/__tests__/fixtures/config-per-file/a/b/c/.stylelintrc diff --git a/src/__tests__/fixtures/config-per-file/a/b/c/c.css b/lib/__tests__/fixtures/config-per-file/a/b/c/c.css similarity index 100% rename from src/__tests__/fixtures/config-per-file/a/b/c/c.css rename to lib/__tests__/fixtures/config-per-file/a/b/c/c.css diff --git a/src/__tests__/fixtures/config-per-file/a/b/c/d/d.css b/lib/__tests__/fixtures/config-per-file/a/b/c/d/d.css similarity index 100% rename from src/__tests__/fixtures/config-per-file/a/b/c/d/d.css rename to lib/__tests__/fixtures/config-per-file/a/b/c/d/d.css diff --git a/src/__tests__/fixtures/config-plugin-extending-with-plugin.json b/lib/__tests__/fixtures/config-plugin-extending-with-plugin.json similarity index 100% rename from src/__tests__/fixtures/config-plugin-extending-with-plugin.json rename to lib/__tests__/fixtures/config-plugin-extending-with-plugin.json diff --git a/src/__tests__/fixtures/config-relative-plugin-nested.json b/lib/__tests__/fixtures/config-relative-plugin-nested.json similarity index 100% rename from src/__tests__/fixtures/config-relative-plugin-nested.json rename to lib/__tests__/fixtures/config-relative-plugin-nested.json diff --git a/src/__tests__/fixtures/config-relative-plugin.json b/lib/__tests__/fixtures/config-relative-plugin.json similarity index 100% rename from src/__tests__/fixtures/config-relative-plugin.json rename to lib/__tests__/fixtures/config-relative-plugin.json diff --git a/src/__tests__/fixtures/config-string-quotes-single.json b/lib/__tests__/fixtures/config-string-quotes-single.json similarity index 100% rename from src/__tests__/fixtures/config-string-quotes-single.json rename to lib/__tests__/fixtures/config-string-quotes-single.json diff --git a/src/__tests__/fixtures/config-with-undefined-rule.json b/lib/__tests__/fixtures/config-with-undefined-rule.json similarity index 100% rename from src/__tests__/fixtures/config-with-undefined-rule.json rename to lib/__tests__/fixtures/config-with-undefined-rule.json diff --git a/src/__tests__/fixtures/config-without-rules.json b/lib/__tests__/fixtures/config-without-rules.json similarity index 100% rename from src/__tests__/fixtures/config-without-rules.json rename to lib/__tests__/fixtures/config-without-rules.json diff --git a/src/__tests__/fixtures/custom-parser.js b/lib/__tests__/fixtures/custom-parser.js similarity index 81% rename from src/__tests__/fixtures/custom-parser.js rename to lib/__tests__/fixtures/custom-parser.js index 98ae933c7e..3d51599291 100644 --- a/src/__tests__/fixtures/custom-parser.js +++ b/lib/__tests__/fixtures/custom-parser.js @@ -1 +1,3 @@ +"use strict" + module.exports = require("postcss-safe-parser/lib/safe-parse") diff --git a/src/__tests__/fixtures/custom-syntax.js b/lib/__tests__/fixtures/custom-syntax.js similarity index 80% rename from src/__tests__/fixtures/custom-syntax.js rename to lib/__tests__/fixtures/custom-syntax.js index 0e95ed9650..26f580f8a4 100644 --- a/src/__tests__/fixtures/custom-syntax.js +++ b/lib/__tests__/fixtures/custom-syntax.js @@ -1 +1,3 @@ +"use strict" + module.exports = require("postcss-scss/lib/scss-syntax") diff --git a/src/__tests__/fixtures/empty-block-with-disables.css b/lib/__tests__/fixtures/empty-block-with-disables.css similarity index 100% rename from src/__tests__/fixtures/empty-block-with-disables.css rename to lib/__tests__/fixtures/empty-block-with-disables.css diff --git a/src/__tests__/fixtures/empty-block.css b/lib/__tests__/fixtures/empty-block.css similarity index 100% rename from src/__tests__/fixtures/empty-block.css rename to lib/__tests__/fixtures/empty-block.css diff --git a/src/__tests__/fixtures/extension-sensitive.less b/lib/__tests__/fixtures/extension-sensitive.less similarity index 100% rename from src/__tests__/fixtures/extension-sensitive.less rename to lib/__tests__/fixtures/extension-sensitive.less diff --git a/src/__tests__/fixtures/extension-sensitive.scss b/lib/__tests__/fixtures/extension-sensitive.scss similarity index 100% rename from src/__tests__/fixtures/extension-sensitive.scss rename to lib/__tests__/fixtures/extension-sensitive.scss diff --git a/src/__tests__/fixtures/extension-sensitive.sss b/lib/__tests__/fixtures/extension-sensitive.sss similarity index 100% rename from src/__tests__/fixtures/extension-sensitive.sss rename to lib/__tests__/fixtures/extension-sensitive.sss diff --git a/src/__tests__/fixtures/getConfigForFile/a/.stylelintrc b/lib/__tests__/fixtures/getConfigForFile/a/.stylelintrc similarity index 100% rename from src/__tests__/fixtures/getConfigForFile/a/.stylelintrc rename to lib/__tests__/fixtures/getConfigForFile/a/.stylelintrc diff --git a/src/__tests__/fixtures/getConfigForFile/a/b/foo.css b/lib/__tests__/fixtures/getConfigForFile/a/b/foo.css similarity index 100% rename from src/__tests__/fixtures/getConfigForFile/a/b/foo.css rename to lib/__tests__/fixtures/getConfigForFile/a/b/foo.css diff --git a/src/__tests__/fixtures/ignore.txt b/lib/__tests__/fixtures/ignore.txt similarity index 100% rename from src/__tests__/fixtures/ignore.txt rename to lib/__tests__/fixtures/ignore.txt diff --git a/src/__tests__/fixtures/invalid-hex.css b/lib/__tests__/fixtures/invalid-hex.css similarity index 100% rename from src/__tests__/fixtures/invalid-hex.css rename to lib/__tests__/fixtures/invalid-hex.css diff --git a/src/__tests__/fixtures/invalid-hex.html b/lib/__tests__/fixtures/invalid-hex.html similarity index 100% rename from src/__tests__/fixtures/invalid-hex.html rename to lib/__tests__/fixtures/invalid-hex.html diff --git a/src/__tests__/fixtures/needlessDisables/.stylelintignore b/lib/__tests__/fixtures/needlessDisables/.stylelintignore similarity index 100% rename from src/__tests__/fixtures/needlessDisables/.stylelintignore rename to lib/__tests__/fixtures/needlessDisables/.stylelintignore diff --git a/src/__tests__/fixtures/needlessDisables/disabled-ranges-1.css b/lib/__tests__/fixtures/needlessDisables/disabled-ranges-1.css similarity index 100% rename from src/__tests__/fixtures/needlessDisables/disabled-ranges-1.css rename to lib/__tests__/fixtures/needlessDisables/disabled-ranges-1.css diff --git a/src/__tests__/fixtures/needlessDisables/disabled-ranges-2.css b/lib/__tests__/fixtures/needlessDisables/disabled-ranges-2.css similarity index 100% rename from src/__tests__/fixtures/needlessDisables/disabled-ranges-2.css rename to lib/__tests__/fixtures/needlessDisables/disabled-ranges-2.css diff --git a/src/__tests__/fixtures/needlessDisables/disabled-ranges-3.css b/lib/__tests__/fixtures/needlessDisables/disabled-ranges-3.css similarity index 100% rename from src/__tests__/fixtures/needlessDisables/disabled-ranges-3.css rename to lib/__tests__/fixtures/needlessDisables/disabled-ranges-3.css diff --git a/src/__tests__/fixtures/needlessDisables/ignored-file.css b/lib/__tests__/fixtures/needlessDisables/ignored-file.css similarity index 100% rename from src/__tests__/fixtures/needlessDisables/ignored-file.css rename to lib/__tests__/fixtures/needlessDisables/ignored-file.css diff --git a/src/__tests__/fixtures/nested-plugin/plugin-nested-warn-about-foo.js b/lib/__tests__/fixtures/nested-plugin/plugin-nested-warn-about-foo.js similarity index 79% rename from src/__tests__/fixtures/nested-plugin/plugin-nested-warn-about-foo.js rename to lib/__tests__/fixtures/nested-plugin/plugin-nested-warn-about-foo.js index 99c35dd946..a841628e9d 100644 --- a/src/__tests__/fixtures/nested-plugin/plugin-nested-warn-about-foo.js +++ b/lib/__tests__/fixtures/nested-plugin/plugin-nested-warn-about-foo.js @@ -1,4 +1,6 @@ -import stylelint from "../../../" +"use strict" + +const stylelint = require("../../../") const ruleName = "plugin/warn-about-foo" @@ -6,7 +8,7 @@ const warnAboutFooMessages = stylelint.utils.ruleMessages("plugin/warn-about-foo found: "found .foo", }) -export default stylelint.createPlugin(ruleName, function (expectation) { +module.exports = stylelint.createPlugin(ruleName, function (expectation) { return (root, result) => { root.walkRules(rule => { if (rule.selector === ".foo") { diff --git a/lib/__tests__/fixtures/plugin-array.js b/lib/__tests__/fixtures/plugin-array.js new file mode 100644 index 0000000000..390fe14da9 --- /dev/null +++ b/lib/__tests__/fixtures/plugin-array.js @@ -0,0 +1,6 @@ +"use strict" + +const conditionallyCheckColorHexCase = require("./plugin-conditionally-check-color-hex-case") +const warnAboutFoo = require("./plugin-warn-about-foo") + +module.exports = [ conditionallyCheckColorHexCase, warnAboutFoo ] diff --git a/src/__tests__/fixtures/plugin-conditionally-check-color-hex-case.js b/lib/__tests__/fixtures/plugin-conditionally-check-color-hex-case.js similarity index 69% rename from src/__tests__/fixtures/plugin-conditionally-check-color-hex-case.js rename to lib/__tests__/fixtures/plugin-conditionally-check-color-hex-case.js index 85e2cb0336..168d36f0ab 100644 --- a/src/__tests__/fixtures/plugin-conditionally-check-color-hex-case.js +++ b/lib/__tests__/fixtures/plugin-conditionally-check-color-hex-case.js @@ -1,8 +1,10 @@ -import stylelint from "../../" +"use strict" + +const stylelint = require("../../") const ruleName = "plugin/conditionally-check-color-hex-case" -export default stylelint.createPlugin(ruleName, function (expectation) { +module.exports = stylelint.createPlugin(ruleName, function (expectation) { const colorHexCaseRule = stylelint.rules["color-hex-case"](expectation) return (root, result) => { if (root.toString().indexOf("@@check-color-hex-case") === -1) return diff --git a/src/__tests__/fixtures/plugin-primary-array.js b/lib/__tests__/fixtures/plugin-primary-array.js similarity index 74% rename from src/__tests__/fixtures/plugin-primary-array.js rename to lib/__tests__/fixtures/plugin-primary-array.js index bbe2e04c9c..643b1d37ad 100644 --- a/src/__tests__/fixtures/plugin-primary-array.js +++ b/lib/__tests__/fixtures/plugin-primary-array.js @@ -1,4 +1,6 @@ -import stylelint from "../../" +"use strict" + +const stylelint = require("../../") const ruleName = "plugin/primary-array" @@ -17,4 +19,4 @@ function rule(primary) { rule.primaryOptionArray = true -export default stylelint.createPlugin(ruleName, rule) +module.exports = stylelint.createPlugin(ruleName, rule) diff --git a/src/__tests__/fixtures/plugin-slashless-warn-about-foo.js b/lib/__tests__/fixtures/plugin-slashless-warn-about-foo.js similarity index 79% rename from src/__tests__/fixtures/plugin-slashless-warn-about-foo.js rename to lib/__tests__/fixtures/plugin-slashless-warn-about-foo.js index f088c8d60f..b60f8e4a5c 100644 --- a/src/__tests__/fixtures/plugin-slashless-warn-about-foo.js +++ b/lib/__tests__/fixtures/plugin-slashless-warn-about-foo.js @@ -1,4 +1,6 @@ -import stylelint from "../../" +"use strict" + +const stylelint = require("../../") const ruleName = "slashless-warn-about-foo" @@ -6,7 +8,7 @@ const warnAboutFooMessages = stylelint.utils.ruleMessages("slashless-warn-about- found: "found .foo", }) -export default stylelint.createPlugin(ruleName, function (expectation) { +module.exports = stylelint.createPlugin(ruleName, function (expectation) { return (root, result) => { root.walkRules(rule => { if (rule.selector === ".foo") { diff --git a/src/__tests__/fixtures/plugin-warn-about-bar.js b/lib/__tests__/fixtures/plugin-warn-about-bar.js similarity index 79% rename from src/__tests__/fixtures/plugin-warn-about-bar.js rename to lib/__tests__/fixtures/plugin-warn-about-bar.js index ea61a202e6..d0c5431021 100644 --- a/src/__tests__/fixtures/plugin-warn-about-bar.js +++ b/lib/__tests__/fixtures/plugin-warn-about-bar.js @@ -1,4 +1,6 @@ -import stylelint from "../../" +"use strict" + +const stylelint = require("../../") const ruleName = "plugin/warn-about-bar" @@ -6,7 +8,7 @@ const warnAboutBarMessages = stylelint.utils.ruleMessages("plugin/warn-about-bar found: "found .bar", }) -export default stylelint.createPlugin(ruleName, function (expectation) { +module.exports = stylelint.createPlugin(ruleName, function (expectation) { return (root, result) => { root.walkRules(rule => { if (rule.selector === ".bar") { diff --git a/src/__tests__/fixtures/plugin-warn-about-foo.js b/lib/__tests__/fixtures/plugin-warn-about-foo.js similarity index 79% rename from src/__tests__/fixtures/plugin-warn-about-foo.js rename to lib/__tests__/fixtures/plugin-warn-about-foo.js index facc12ff23..f42c80ed7b 100644 --- a/src/__tests__/fixtures/plugin-warn-about-foo.js +++ b/lib/__tests__/fixtures/plugin-warn-about-foo.js @@ -1,4 +1,6 @@ -import stylelint from "../../" +"use strict" + +const stylelint = require("../../") const ruleName = "plugin/warn-about-foo" @@ -6,7 +8,7 @@ const warnAboutFooMessages = stylelint.utils.ruleMessages("plugin/warn-about-foo found: "found .foo", }) -export default stylelint.createPlugin(ruleName, function (expectation) { +module.exports = stylelint.createPlugin(ruleName, function (expectation) { return (root, result) => { root.walkRules(rule => { if (rule.selector === ".foo") { diff --git a/src/__tests__/fixtures/processor-fenced-blocks.js b/lib/__tests__/fixtures/processor-fenced-blocks.js similarity index 55% rename from src/__tests__/fixtures/processor-fenced-blocks.js rename to lib/__tests__/fixtures/processor-fenced-blocks.js index b2d5b39c3b..d8bb6ac683 100644 --- a/src/__tests__/fixtures/processor-fenced-blocks.js +++ b/lib/__tests__/fixtures/processor-fenced-blocks.js @@ -1,11 +1,15 @@ -import execall from "execall" +"use strict" + +const execall = require("execall") + +module.exports = function () { + const options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {} -export default function (options = {}) { const specialMessage = options.specialMessage || "was processed" return { code(input) { const blocks = execall(/```start([\s\S]+?)```end/g, input) - const toLint = blocks.map((match) => match.sub[0]).join("\n\n") + const toLint = blocks.map(match => match.sub[0]).join("\n\n") return toLint }, result(stylelintResult) { diff --git a/src/__tests__/fixtures/processor-triple-question-marks.js b/lib/__tests__/fixtures/processor-triple-question-marks.js similarity index 70% rename from src/__tests__/fixtures/processor-triple-question-marks.js rename to lib/__tests__/fixtures/processor-triple-question-marks.js index 65ec77de37..37cdf63a7b 100644 --- a/src/__tests__/fixtures/processor-triple-question-marks.js +++ b/lib/__tests__/fixtures/processor-triple-question-marks.js @@ -1,11 +1,13 @@ -import execall from "execall" +"use strict" -export default function () { +const execall = require("execall") + +module.exports = function () { let found = false return { code(input) { const blocks = execall(/\?\?\?start([\s\S]+?)\?\?\?end/g, input) - const toLint = blocks.map((match) => match.sub[0]).join("\n\n") + const toLint = blocks.map(match => match.sub[0]).join("\n\n") if (toLint.length > 0) { found = true } diff --git a/src/__tests__/fixtures/standaloneNoParsing/no-syntax-error.css b/lib/__tests__/fixtures/standaloneNoParsing/no-syntax-error.css similarity index 100% rename from src/__tests__/fixtures/standaloneNoParsing/no-syntax-error.css rename to lib/__tests__/fixtures/standaloneNoParsing/no-syntax-error.css diff --git a/src/__tests__/fixtures/standaloneNoParsing/syntax-error-ignored.scss b/lib/__tests__/fixtures/standaloneNoParsing/syntax-error-ignored.scss similarity index 100% rename from src/__tests__/fixtures/standaloneNoParsing/syntax-error-ignored.scss rename to lib/__tests__/fixtures/standaloneNoParsing/syntax-error-ignored.scss diff --git a/src/__tests__/ignore.test.js b/lib/__tests__/ignore.test.js similarity index 89% rename from src/__tests__/ignore.test.js rename to lib/__tests__/ignore.test.js index ab28e3db4a..a8c427725d 100644 --- a/src/__tests__/ignore.test.js +++ b/lib/__tests__/ignore.test.js @@ -1,6 +1,8 @@ -import _ from "lodash" -import path from "path" -import standalone from "../standalone" +"use strict" + +const _ = require("lodash") +const path = require("path") +const standalone = require("../standalone") const fixturesPath = path.join(__dirname, "fixtures") @@ -12,13 +14,10 @@ describe("extending config and ignoreFiles glob ignoring single glob", () => { files: [ `${fixturesPath}/empty-block.css`, `${fixturesPath}/invalid-hex.css` ], config: { ignoreFiles: "**/invalid-hex.css", - extends: [ - "./config-block-no-empty", - "./config-color-no-invalid-hex", - ], + extends: [ "./config-block-no-empty", "./config-color-no-invalid-hex" ], }, configBasedir: path.join(__dirname, "fixtures"), - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("two files found", () => { @@ -68,12 +67,9 @@ describe("same as above with no configBasedir, ignore-files path relative to pro files: [ `${fixturesPath}/empty-block.css`, `${fixturesPath}/invalid-hex.css` ], config: { ignoreFiles: "fixtures/invalid-hex.css", - extends: [ - `${fixturesPath}/config-block-no-empty.json`, - `${fixturesPath}/config-color-no-invalid-hex`, - ], + extends: [ `${fixturesPath}/config-block-no-empty.json`, `${fixturesPath}/config-color-no-invalid-hex` ], }, - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("two files found", () => { @@ -118,7 +114,7 @@ describe("absolute ignoreFiles glob path", () => { }, }, configBasedir: path.join(__dirname, "fixtures"), - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("two files found", () => { @@ -157,17 +153,11 @@ describe("extending config with ignoreFiles glob ignoring one by negation", () = return standalone({ files: [ `${fixturesPath}/empty-block.css`, `${fixturesPath}/invalid-hex.css` ], config: { - ignoreFiles: [ - "**/*.css", - "!**/invalid-hex.css", - ], - extends: [ - `${fixturesPath}/config-block-no-empty`, - `${fixturesPath}/config-color-no-invalid-hex`, - ], + ignoreFiles: [ "**/*.css", "!**/invalid-hex.css" ], + extends: [ `${fixturesPath}/config-block-no-empty`, `${fixturesPath}/config-color-no-invalid-hex` ], }, configBasedir: path.join(__dirname, "fixtures"), - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("two files found", () => { @@ -229,7 +219,7 @@ describe("specified `ignorePath` file ignoring one file", () => { }, }, ignorePath: path.join(__dirname, "fixtures/ignore.txt"), - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("no warnings registered", () => { @@ -255,7 +245,7 @@ describe("using ignoreFiles with input files that would cause a postcss syntax e "block-no-empty": true, }, }, - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("two files found", () => { @@ -294,12 +284,10 @@ describe("extending a config that ignores files", () => { return standalone({ files: [ `${fixturesPath}/empty-block.css`, `${fixturesPath}/invalid-hex.css` ], config: { - extends: [ - `${fixturesPath}/config-extending-and-ignoring`, - ], + extends: [`${fixturesPath}/config-extending-and-ignoring`], }, configBasedir: path.join(__dirname, "fixtures"), - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("found correct files", () => { @@ -334,7 +322,7 @@ describe("using codeFilename and ignoreFiles together", () => { ignoreFiles: ["**/foo.css"], rules: { "block-no-empty": true }, }, - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("no warnings", () => { @@ -362,7 +350,7 @@ describe("using codeFilename and ignoreFiles with configBasedir", () => { rules: { "block-no-empty": true }, }, configBasedir: __dirname, - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("no warnings", () => { diff --git a/src/__tests__/ignoreDisables.test.js b/lib/__tests__/ignoreDisables.test.js similarity index 88% rename from src/__tests__/ignoreDisables.test.js rename to lib/__tests__/ignoreDisables.test.js index 2e7d482ace..a6bf1afaeb 100644 --- a/src/__tests__/ignoreDisables.test.js +++ b/lib/__tests__/ignoreDisables.test.js @@ -1,6 +1,8 @@ -import postcssPlugin from "../postcssPlugin" -import standalone from "../standalone" -import { stripIndent } from "common-tags" +"use strict" + +const postcssPlugin = require("../postcssPlugin") +const standalone = require("../standalone") +const stripIndent = require("common-tags").stripIndent const config = { rules: { "block-no-empty": true }, diff --git a/src/__tests__/integration.test.js b/lib/__tests__/integration.test.js similarity index 79% rename from src/__tests__/integration.test.js rename to lib/__tests__/integration.test.js index 11b74e0498..67d2a407d4 100644 --- a/src/__tests__/integration.test.js +++ b/lib/__tests__/integration.test.js @@ -1,25 +1,19 @@ -import lessSyntax from "postcss-less" -import path from "path" -import postcss from "postcss" -import scssSyntax from "postcss-scss" -import stylelint from "../" +"use strict" + +const lessSyntax = require("postcss-less") +const path = require("path") +const postcss = require("postcss") +const scssSyntax = require("postcss-scss") +const stylelint = require("../") const config = { rules: { "block-opening-brace-newline-after": "always", - "declaration-block-properties-order": [ - { - properties: [ - "content", - ], - }, - { - properties: [ - "display", - "height", - ], - }, - ], + "declaration-block-properties-order": [ { + properties: ["content"], + }, { + properties: [ "display", "height" ], + } ], "declaration-property-unit-blacklist": { "width": [ "px", "em" ], }, @@ -33,8 +27,7 @@ const config = { }, } -const css = ( -` +const css = ` a { background: pink; } b { @@ -54,16 +47,13 @@ b { color: #mmm; height: calc(1rem - 100%) } -`) +` describe("integration test expecting warnings", () => { let result beforeEach(() => { - return postcss() - .use(stylelint(config)) - .process(css) - .then(data => result = data) + return postcss().use(stylelint(config)).process(css).then(data => result = data) }) it("number and type", () => { @@ -101,33 +91,25 @@ describe("integration test expecting warnings", () => { }) it("Less integration test", () => { - const less = ( - `.foo(@bar) { + const less = `.foo(@bar) { color: @bar; } - `) - - return postcss() - .use(stylelint({ rules: {} })) - .process(less, { syntax: lessSyntax }) - .then((result) => { - expect(result.messages.length).toBe(0) - }) + ` + + return postcss().use(stylelint({ rules: {} })).process(less, { syntax: lessSyntax }).then(result => { + expect(result.messages.length).toBe(0) + }) }) it("Scss integration test", () => { - const scss = ( - `.foo-#{variable} { + const scss = `.foo-#{variable} { color: $color; } - `) - - return postcss() - .use(stylelint({ rules: {} })) - .process(scss, { syntax: scssSyntax }) - .then((result) => { - expect(result.messages.length).toBe(0) - }) + ` + + return postcss().use(stylelint({ rules: {} })).process(scss, { syntax: scssSyntax }).then(result => { + expect(result.messages.length).toBe(0) + }) }) describe("integration test null option", () => { diff --git a/lib/__tests__/message.test.js b/lib/__tests__/message.test.js new file mode 100644 index 0000000000..b31e4b7d4c --- /dev/null +++ b/lib/__tests__/message.test.js @@ -0,0 +1,14 @@ +"use strict" + +const path = require("path") +const standalone = require("../standalone") + +it("standalone loading YAML with custom message", () => { + return standalone({ + code: "a { color: pink; }", + configFile: path.join(__dirname, "fixtures/config-color-named-custom-message.yaml"), + }).then((linted) => { + expect(linted.results[0].warnings.length).toBe(1) + expect(linted.results[0].warnings[0].text).toBe("Unacceptable") + }) +}) diff --git a/lib/__tests__/needlessDisables.test.js b/lib/__tests__/needlessDisables.test.js new file mode 100644 index 0000000000..6418c9b73f --- /dev/null +++ b/lib/__tests__/needlessDisables.test.js @@ -0,0 +1,86 @@ +"use strict" + +const needlessDisables = require("../needlessDisables") +const path = require("path") +const standalone = require("../standalone") +const stripIndent = require("common-tags").stripIndent + +function fixture(name) { + return path.join(__dirname, "./fixtures/needlessDisables", name) +} + +it("needlessDisables simple case", () => { + const config = { + rules: { "block-no-empty": true }, + } + + const css = stripIndent` + /* stylelint-disable */ + a {} + /* stylelint-enable */ + a { + b {} /* stylelint-disable-line block-no-empty */ + } + /* stylelint-disable */ + a { color: pink; } + /* stylelint-enable */ + a { + b { color: pink; } /* stylelint-disable-line block-no-empty */ + } + ` + + return standalone({ + config, + code: css, + ignoreDisables: true, + }).then((linted) => { + const report = needlessDisables(linted.results) + expect(report.length).toBe(1) + expect(report[0].ranges).toEqual([ { start: 7, end: 9 }, { start: 11, end: 11 } ]) + }) +}) + +it("needlessDisables complex case", () => { + const config = { + rules: { + "block-no-empty": true, + "color-named": "never", + }, + } + + return standalone({ + config, + files: [ fixture("disabled-ranges-1.css"), fixture("disabled-ranges-2.css"), + // ignore files contain `CssSyntaxError` + fixture("disabled-ranges-3.css") ], + ignoreDisables: true, + }).then((linted) => { + expect(needlessDisables(linted.results)).toEqual([ { + source: fixture("disabled-ranges-1.css"), + ranges: [ { start: 1, end: 3 }, { start: 5, end: 7 }, { start: 10, end: 10 } ], + }, { + source: fixture("disabled-ranges-2.css"), + ranges: [ { start: 5, end: 5 }, { start: 6, end: 6 }, { start: 8 } ], + } ]) + }) +}) + +it("needlessDisables ignored case", () => { + const config = { + rules: { + "block-no-empty": true, + }, + } + + return standalone({ + config, + files: [ fixture("disabled-ranges-1.css"), fixture("ignored-file.css") ], + ignoreDisables: true, + ignorePath: fixture(".stylelintignore"), + }).then((linted) => { + expect(needlessDisables(linted.results)).toEqual([{ + source: fixture("disabled-ranges-1.css"), + ranges: [ { start: 1, end: 3 }, { start: 5, end: 7 }, { start: 10, end: 10 } ], + }]) + }) +}) diff --git a/src/__tests__/normalizeRuleSettings-integration.test.js b/lib/__tests__/normalizeRuleSettings-integration.test.js similarity index 71% rename from src/__tests__/normalizeRuleSettings-integration.test.js rename to lib/__tests__/normalizeRuleSettings-integration.test.js index 859f6bb399..029cd8f8e1 100644 --- a/src/__tests__/normalizeRuleSettings-integration.test.js +++ b/lib/__tests__/normalizeRuleSettings-integration.test.js @@ -1,5 +1,7 @@ -import path from "path" -import standalone from "../standalone" +"use strict" + +const path = require("path") +const standalone = require("../standalone") it("[normalized rule settings] primary option array", () => { return standalone({ @@ -9,8 +11,8 @@ it("[normalized rule settings] primary option array", () => { "selector-pseudo-class-blacklist": ["focus"], }, }, - }).then(({ results }) => { - expect(results[0].warnings[0].rule).toBe("selector-pseudo-class-blacklist") + }).then((linted) => { + expect(linted.results[0].warnings[0].rule).toBe("selector-pseudo-class-blacklist") }) }) @@ -22,8 +24,8 @@ it("[normalized rule settings] primary option array in array", () => { "selector-pseudo-class-blacklist": [["focus"]], }, }, - }).then(({ results }) => { - expect(results[0].warnings[0].rule).toBe("selector-pseudo-class-blacklist") + }).then((linted) => { + expect(linted.results[0].warnings[0].rule).toBe("selector-pseudo-class-blacklist") }) }) @@ -36,8 +38,8 @@ it("[normalized rule settings] no-array primary, primary option null", () => { "block-no-empty": null, }, }, - }).then(({ results }) => { - expect(results[0].warnings.length).toBe(0) + }).then((linted) => { + expect(linted.results[0].warnings.length).toBe(0) }) }) @@ -50,8 +52,8 @@ it("[normalized rule settings] no-array primary, primary option null in array", "block-no-empty": [null], }, }, - }).then(({ results }) => { - expect(results[0].warnings.length).toBe(0) + }).then((linted) => { + expect(linted.results[0].warnings.length).toBe(0) }) }) @@ -64,8 +66,8 @@ it("[normalized rule settings] array primary, primary option null", () => { "unit-blacklist": null, }, }, - }).then(({ results }) => { - expect(results[0].warnings.length).toBe(0) + }).then((linted) => { + expect(linted.results[0].warnings.length).toBe(0) }) }) @@ -78,7 +80,7 @@ it("[normalized rule settings] array primary, primary option null in array", () "unit-blacklist": [null], }, }, - }).then(({ results }) => { - expect(results[0].invalidOptionWarnings.length).toBe(0) + }).then((linted) => { + expect(linted.results[0].invalidOptionWarnings.length).toBe(0) }) }) diff --git a/src/__tests__/normalizeRuleSettings.test.js b/lib/__tests__/normalizeRuleSettings.test.js similarity index 98% rename from src/__tests__/normalizeRuleSettings.test.js rename to lib/__tests__/normalizeRuleSettings.test.js index 65bc569957..285c33ba83 100644 --- a/src/__tests__/normalizeRuleSettings.test.js +++ b/lib/__tests__/normalizeRuleSettings.test.js @@ -1,4 +1,6 @@ -import normalizeRuleSettings from "../normalizeRuleSettings" +"use strict" + +const normalizeRuleSettings = require("../normalizeRuleSettings") describe("rules whose primary option IS NOT an array", () => { it("solo null returns null", () => { diff --git a/src/__tests__/plugins.test.js b/lib/__tests__/plugins.test.js similarity index 86% rename from src/__tests__/plugins.test.js rename to lib/__tests__/plugins.test.js index 074c225a00..1984350aa7 100644 --- a/src/__tests__/plugins.test.js +++ b/lib/__tests__/plugins.test.js @@ -1,23 +1,17 @@ -import path from "path" -import postcss from "postcss" -import stylelint from ".." +"use strict" -const cssWithFoo = ( -".foo {}" -) +const path = require("path") +const postcss = require("postcss") +const stylelint = require("..") -const cssWithoutFoo = ( -".bar {}" -) +const cssWithFoo = ".foo {}" -const cssWithFooAndBar = ( -".foo {}\n.bar {}" -) +const cssWithoutFoo = ".bar {}" + +const cssWithFooAndBar = ".foo {}\n.bar {}" const configRelative = { - plugins: [ - "./fixtures/plugin-warn-about-foo", - ], + plugins: ["./fixtures/plugin-warn-about-foo"], rules: { "plugin/warn-about-foo": "always", "block-no-empty": true, @@ -25,9 +19,7 @@ const configRelative = { } const configAbsolute = { - plugins: [ - path.join(__dirname, "./fixtures/plugin-warn-about-foo"), - ], + plugins: [path.join(__dirname, "./fixtures/plugin-warn-about-foo")], rules: { "plugin/warn-about-foo": "always", "block-no-empty": true, @@ -35,18 +27,12 @@ const configAbsolute = { } const configExtendRelative = { - extends: [ - "./fixtures/config-relative-plugin", - ], + extends: ["./fixtures/config-relative-plugin"], } const configRelativeAndExtendRelative = { - extends: [ - "./fixtures/config-relative-plugin", - ], - plugins: [ - "./fixtures/plugin-warn-about-bar", - ], + extends: ["./fixtures/config-relative-plugin"], + plugins: ["./fixtures/plugin-warn-about-bar"], rules: { "plugin/warn-about-bar": "always", }, @@ -182,13 +168,11 @@ it("slashless plugin causes configuration error", () => { }, } - return postcss().use(stylelint(config)).process(".foo {}") - .then(() => { - throw new Error("should not have succeeded") - }) - .catch(err => { - expect(err.message.indexOf("stylelint v7+ requires plugin rules to be namspaced")).toBe(0) - }) + return postcss().use(stylelint(config)).process(".foo {}").then(() => { + throw new Error("should not have succeeded") + }).catch(err => { + expect(err.message.indexOf("stylelint v7+ requires plugin rules to be namspaced")).toBe(0) + }) }) it("plugin with primary option array", () => { @@ -198,7 +182,7 @@ it("plugin with primary option array", () => { "plugin/primary-array": [ "foo", "bar" ], }, } - return postcss().use((stylelint(config))).process("a {}").then(result => { + return postcss().use(stylelint(config)).process("a {}").then(result => { expect(result.warnings().length).toBe(0) }) }) @@ -210,7 +194,7 @@ it("plugin with primary option array within options array", () => { "plugin/primary-array": [ [ "foo", "bar" ], { something: true } ], }, } - return postcss().use((stylelint(config))).process("a {}").then(result => { + return postcss().use(stylelint(config)).process("a {}").then(result => { expect(result.warnings().length).toBe(0) }) }) @@ -236,10 +220,7 @@ describe("loading a plugin from process.cwd", () => { }) beforeEach(() => { - return postcss() - .use((stylelint(config))) - .process(".foo {}") - .then(data => result = data) + return postcss().use(stylelint(config)).process(".foo {}").then(data => result = data) }) it("error is caught", () => { diff --git a/src/__tests__/postcssPlugin.test.js b/lib/__tests__/postcssPlugin.test.js similarity index 81% rename from src/__tests__/postcssPlugin.test.js rename to lib/__tests__/postcssPlugin.test.js index 014e0659f8..2daf51d8d0 100644 --- a/src/__tests__/postcssPlugin.test.js +++ b/lib/__tests__/postcssPlugin.test.js @@ -1,11 +1,13 @@ -import { configurationError } from "../utils" -import path from "path" -import postcssPlugin from "../postcssPlugin" +"use strict" + +const configurationError = require("../utils/configurationError") +const path = require("path") +const postcssPlugin = require("../postcssPlugin") it("`config` option is `null`", () => { return postcssPlugin.process("a {}").then(() => { throw new Error("should not have succeeded") - }).catch((err) => { + }).catch(err => { expect(err.message.indexOf("No configuration provided")).not.toBe(-1) }) }) @@ -14,7 +16,7 @@ it("`configFile` option with absolute path", () => { const config = { configFile: path.join(__dirname, "fixtures/config-block-no-empty.json"), } - return postcssPlugin.process("a {}", config).then((postcssResult) => { + return postcssPlugin.process("a {}", config).then(postcssResult => { const warnings = postcssResult.warnings() expect(warnings.length).toBe(1) expect(warnings[0].text.indexOf("block-no-empty")).not.toBe(-1) @@ -24,7 +26,7 @@ it("`configFile` option with absolute path", () => { it("`configFile` with bad path", () => { return postcssPlugin.process("a {}", { configFile: "./herby.json" }).then(() => { throw new Error("should not have succeeded") - }).catch((err) => { + }).catch(err => { expect(err.code).toBe("ENOENT") }) }) @@ -35,7 +37,7 @@ it("`configFile` option without rules", () => { } return postcssPlugin.process("a {}", config).then(() => { throw new Error("should not have succeeded") - }).catch((err) => { + }).catch(err => { expect(err).toEqual(configurationError("No rules found within configuration. Have you provided a \"rules\" property?")) }) }) @@ -47,7 +49,7 @@ it("`configFile` option with undefined rule", () => { const ruleName = "unknown-rule" return postcssPlugin.process("a {}", config).then(() => { throw new Error("should not have succeeded") - }).catch((err) => { + }).catch(err => { expect(err).toEqual(configurationError(`Undefined rule ${ruleName}`)) }) }) @@ -60,7 +62,7 @@ it("`ignoreFiles` options is not empty and file ignored", () => { ignoreFiles: "**/foo.css", from: "foo.css", } - return postcssPlugin.process("a {}", config).then((postcssResult) => { + return postcssPlugin.process("a {}", config).then(postcssResult => { expect(postcssResult.stylelint.ignored).toBeTruthy() }) }) @@ -73,7 +75,7 @@ it("`ignoreFiles` options is not empty and file not ignored", () => { ignoreFiles: "**/bar.css", from: "foo.css", } - return postcssPlugin.process("a {}", config).then((postcssResult) => { + return postcssPlugin.process("a {}", config).then(postcssResult => { expect(postcssResult.stylelint.ignored).toBeFalsy() }) }) diff --git a/src/__tests__/processors.test.js b/lib/__tests__/processors.test.js similarity index 77% rename from src/__tests__/processors.test.js rename to lib/__tests__/processors.test.js index d354779332..5b1f6446ae 100644 --- a/src/__tests__/processors.test.js +++ b/lib/__tests__/processors.test.js @@ -1,5 +1,7 @@ -import path from "path" -import standalone from "../standalone" +"use strict" + +const path = require("path") +const standalone = require("../standalone") const fixturesPath = path.join(__dirname, "./fixtures") @@ -15,7 +17,7 @@ describe("processor transforms input and output", () => { processors: "./processor-fenced-blocks", }, configBasedir: fixturesPath, - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("number of results", () => { @@ -52,12 +54,10 @@ describe("processor accepts options", () => { code, config: { extends: "./config-block-no-empty", - processors: [ - [ "./processor-fenced-blocks", { specialMessage: "options worked" } ], - ], + processors: [[ "./processor-fenced-blocks", { specialMessage: "options worked" } ]], }, configBasedir: fixturesPath, - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("special message", () => { @@ -69,19 +69,15 @@ describe("multiple processors", () => { let results beforeEach(() => { - const code = "one\ntwo\n```start\na {}\nb { color: pink }\n```end\nthree???startc {}???end" + - "\n\n???start```start\na {}\nb { color: pink }\n```end???end" + const code = "one\ntwo\n```start\na {}\nb { color: pink }\n```end\nthree???startc {}???end" + "\n\n???start```start\na {}\nb { color: pink }\n```end???end" return standalone({ code, config: { extends: "./config-block-no-empty", - processors: [ - "./processor-triple-question-marks", - [ "./processor-fenced-blocks", { specialMessage: "options worked" } ], - ], + processors: [ "./processor-triple-question-marks", [ "./processor-fenced-blocks", { specialMessage: "options worked" } ] ], }, configBasedir: fixturesPath, - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("number of results", () => { @@ -127,19 +123,15 @@ describe("loading processors (and extend) from process.cwd", () => { }) beforeEach(() => { - const code = "one\ntwo\n```start\na {}\nb { color: pink }\n```end\nthree???startc {}???end" + - "\n\n???start```start\na {}\nb { color: pink }\n```end???end" + const code = "one\ntwo\n```start\na {}\nb { color: pink }\n```end\nthree???startc {}???end" + "\n\n???start```start\na {}\nb { color: pink }\n```end???end" return standalone({ code, config: { extends: "./__tests__/fixtures/config-block-no-empty", - processors: [ - "./__tests__/fixtures/processor-triple-question-marks", - [ "./__tests__/fixtures/processor-fenced-blocks", { specialMessage: "options worked" } ], - ], + processors: [ "./__tests__/fixtures/processor-triple-question-marks", [ "./__tests__/fixtures/processor-fenced-blocks", { specialMessage: "options worked" } ] ], }, - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("number of results", () => { diff --git a/src/__tests__/standalone-formatter.test.js b/lib/__tests__/standalone-formatter.test.js similarity index 65% rename from src/__tests__/standalone-formatter.test.js rename to lib/__tests__/standalone-formatter.test.js index b696b3a2d8..1673c71c62 100644 --- a/src/__tests__/standalone-formatter.test.js +++ b/lib/__tests__/standalone-formatter.test.js @@ -1,10 +1,14 @@ -import chalk from "chalk" -import configBlockNoEmpty from "./fixtures/config-block-no-empty" -import standalone from "../standalone" -import stringFormatter from "../formatters/stringFormatter" +"use strict" + +const chalk = require("chalk") +const configBlockNoEmpty = require("./fixtures/config-block-no-empty") +const standalone = require("../standalone") +const stringFormatter = require("../formatters/stringFormatter") it("standalone with input css and alternate formatter specified by keyword", () => { - return standalone({ code: "a {}", config: configBlockNoEmpty, formatter: "string" }).then(({ output }) => { + return standalone({ code: "a {}", config: configBlockNoEmpty, formatter: "string" }).then((linted) => { + const output = linted.output + const strippedOutput = chalk.stripColor(output) expect(typeof output).toBe("string") expect(strippedOutput.indexOf("1:3")).not.toBe(-1) @@ -13,7 +17,9 @@ it("standalone with input css and alternate formatter specified by keyword", () }) it("standalone with input css and alternate formatter function", () => { - return standalone({ code: "a {}", config: configBlockNoEmpty, formatter: stringFormatter }).then(({ output }) => { + return standalone({ code: "a {}", config: configBlockNoEmpty, formatter: stringFormatter }).then((linted) => { + const output = linted.output + const strippedOutput = chalk.stripColor(output) expect(typeof output).toBe("string") expect(strippedOutput.indexOf("1:3")).not.toBe(-1) diff --git a/src/__tests__/standalone-needlessDisables.test.js b/lib/__tests__/standalone-needlessDisables.test.js similarity index 83% rename from src/__tests__/standalone-needlessDisables.test.js rename to lib/__tests__/standalone-needlessDisables.test.js index 40cc53ecfe..1af88e1027 100644 --- a/src/__tests__/standalone-needlessDisables.test.js +++ b/lib/__tests__/standalone-needlessDisables.test.js @@ -1,5 +1,7 @@ -import path from "path" -import standalone from "../standalone" +"use strict" + +const path = require("path") +const standalone = require("../standalone") const fixturesPath = path.join(__dirname, "fixtures") @@ -16,7 +18,9 @@ it("standalone with input css and `reportNeedlessDisables`", () => { code: "/* stylelint-disable color-named */\na {}", config, reportNeedlessDisables: true, - }).then(({ needlessDisables }) => { + }).then((linted) => { + const needlessDisables = linted.needlessDisables + expect(typeof needlessDisables).toBe("object") expect(needlessDisables.length).toBe(1) expect(needlessDisables[0].ranges.length).toBe(1) @@ -39,7 +43,9 @@ it("standalone with input file(s) and `reportNeedlessDisables`", () => { files: path.join(fixturesPath, "empty-block-with-disables.css"), config, reportNeedlessDisables: true, - }).then(({ needlessDisables }) => { + }).then((linted) => { + const needlessDisables = linted.needlessDisables + expect(typeof needlessDisables).toBe("object") expect(needlessDisables.length).toBe(1) expect(needlessDisables[0].source).toBe(path.join(fixturesPath, "empty-block-with-disables.css")) diff --git a/src/__tests__/standalone-quiet.test.js b/lib/__tests__/standalone-quiet.test.js similarity index 51% rename from src/__tests__/standalone-quiet.test.js rename to lib/__tests__/standalone-quiet.test.js index 3d6691ab14..6c3ca1a4d0 100644 --- a/src/__tests__/standalone-quiet.test.js +++ b/lib/__tests__/standalone-quiet.test.js @@ -1,4 +1,6 @@ -import standalone from "../standalone" +"use strict" + +const standalone = require("../standalone") it("standalone with input css and quiet mode", () => { const config = { @@ -8,7 +10,7 @@ it("standalone with input css and quiet mode", () => { }, } - return standalone({ code: "a {}", config }).then(({ results }) => { - expect(results[0].warnings).toEqual([]) + return standalone({ code: "a {}", config }).then((linted) => { + expect(linted.results[0].warnings).toEqual([]) }) }) diff --git a/src/__tests__/standalone-syntax.test.js b/lib/__tests__/standalone-syntax.test.js similarity index 83% rename from src/__tests__/standalone-syntax.test.js rename to lib/__tests__/standalone-syntax.test.js index ad8b56f15a..a23cb11809 100644 --- a/src/__tests__/standalone-syntax.test.js +++ b/lib/__tests__/standalone-syntax.test.js @@ -1,10 +1,12 @@ -import chalk from "chalk" -import less from "postcss-less" -import path from "path" -import scss from "postcss-scss" -import standalone from "../standalone" -import stringFormatter from "../formatters/stringFormatter" -import sugarss from "sugarss" +"use strict" + +const chalk = require("chalk") +const less = require("postcss-less") +const path = require("path") +const scss = require("postcss-scss") +const standalone = require("../standalone") +const stringFormatter = require("../formatters/stringFormatter") +const sugarss = require("sugarss") const fixturesPath = path.join(__dirname, "fixtures") @@ -20,9 +22,9 @@ it("standalone with scss syntax", () => { code: "$foo: bar; // foo;\nb {}", syntax: "scss", formatter: stringFormatter, - }).then(({ output }) => { - const strippedOutput = chalk.stripColor(output) - expect(typeof output).toBe("string") + }).then((linted) => { + const strippedOutput = chalk.stripColor(linted.output) + expect(typeof linted.output).toBe("string") expect(strippedOutput.indexOf("2:3")).not.toBe(-1) expect(strippedOutput.indexOf("block-no-empty")).not.toBe(-1) }) @@ -40,9 +42,9 @@ it("standalone with sugarss syntax", () => { code: ".one\n color: black\n top: 0px\n.two", syntax: "sugarss", formatter: stringFormatter, - }).then(({ output }) => { - const strippedOutput = chalk.stripColor(output) - expect(typeof output).toBe("string") + }).then((linted) => { + const strippedOutput = chalk.stripColor(linted.output) + expect(typeof linted.output).toBe("string") expect(strippedOutput.indexOf("3:9")).not.toBe(-1) expect(strippedOutput.indexOf("length-zero-no-unit")).not.toBe(-1) }) @@ -60,9 +62,9 @@ it("standalone with Less syntax", () => { code: "@foo: bar; // foo;\nb {}", syntax: "less", formatter: stringFormatter, - }).then(({ output }) => { - const strippedOutput = chalk.stripColor(output) - expect(typeof output).toBe("string") + }).then((linted) => { + const strippedOutput = chalk.stripColor(linted.output) + expect(typeof linted.output).toBe("string") expect(strippedOutput.indexOf("2:3")).not.toBe(-1) expect(strippedOutput.indexOf("block-no-empty")).not.toBe(-1) }) @@ -79,7 +81,7 @@ describe("standalone with syntax set by extension", () => { files: `${fixturesPath}/extension-sensitive.*`, config: { rules: { "block-no-empty": true } }, syntax: "scss", - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("correct number of files", () => { @@ -98,7 +100,7 @@ describe("standalone with syntax set by extension", () => { files: `${fixturesPath}/extension-sensitive.*`, config: { rules: { "block-no-empty": true } }, syntax: "less", - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("correct number of files", () => { @@ -117,7 +119,7 @@ describe("standalone with syntax set by extension", () => { files: `${fixturesPath}/extension-sensitive.*`, config: { rules: { "block-no-empty": true } }, syntax: "sugarss", - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("correct number of files", () => { @@ -135,7 +137,7 @@ describe("standalone with syntax set by extension", () => { return standalone({ files: `${fixturesPath}/extension-sensitive.*`, config: { rules: { "block-no-empty": true } }, - }).then((data) => results = data.results) + }).then(data => results = data.results) }) it("correct number of files", () => { @@ -165,7 +167,9 @@ it("standalone with path to custom parser", () => { customSyntax: `${fixturesPath}/custom-parser`, code: ".foo { width: 200px }\n.bar {", formatter: stringFormatter, - }).then(({ results }) => { + }).then((linted) => { + const results = linted.results + expect(results.length).toBe(1) expect(results[0].warnings.length).toBe(1) expect(results[0].warnings[0].line).toBe(2) @@ -186,7 +190,9 @@ it("standalone with path to custom syntax", () => { customSyntax: `${fixturesPath}/custom-syntax`, code: "$foo: bar; // foo;\nb {}", formatter: stringFormatter, - }).then(({ results }) => { + }).then((linted) => { + const results = linted.results + expect(results.length).toBe(1) expect(results[0].warnings.length).toBe(1) expect(results[0].warnings[0].line).toBe(2) @@ -208,7 +214,9 @@ it("standalone should use customSyntax when both customSyntax and syntax are set customSyntax: `${fixturesPath}/custom-syntax`, code: "$foo: bar; // foo;\nb {}", formatter: stringFormatter, - }).then(({ results }) => { + }).then((linted) => { + const results = linted.results + expect(results.length).toBe(1) expect(results[0].warnings.length).toBe(1) expect(results[0].warnings[0].line).toBe(2) diff --git a/src/__tests__/standalone.test.js b/lib/__tests__/standalone.test.js similarity index 84% rename from src/__tests__/standalone.test.js rename to lib/__tests__/standalone.test.js index c90a142453..197b326647 100644 --- a/src/__tests__/standalone.test.js +++ b/lib/__tests__/standalone.test.js @@ -1,8 +1,10 @@ -import configBlockNoEmpty from "./fixtures/config-block-no-empty" -import path from "path" -import ruleDefinitions from "../rules" -import sinon from "sinon" -import standalone from "../standalone" +"use strict" + +const configBlockNoEmpty = require("./fixtures/config-block-no-empty") +const path = require("path") +const ruleDefinitions = require("../rules") +const sinon = require("sinon") +const standalone = require("../standalone") const fixturesPath = path.join(__dirname, "fixtures") @@ -63,11 +65,11 @@ describe("standalone with two file-specific globs", () => { }) it("standalone with input css", () => { - return standalone({ code: "a {}", config: configBlockNoEmpty }).then(({ output, results }) => { - expect(typeof output).toBe("string") - expect(results.length).toBe(1) - expect(results[0].warnings.length).toBe(1) - expect(results[0].warnings[0].rule).toBe("block-no-empty") + return standalone({ code: "a {}", config: configBlockNoEmpty }).then((linted) => { + expect(typeof linted.output).toBe("string") + expect(linted.results.length).toBe(1) + expect(linted.results[0].warnings.length).toBe(1) + expect(linted.results[0].warnings[0].rule).toBe("block-no-empty") }) }) @@ -85,7 +87,7 @@ it("standalone with non-existent-file should throw error with code 80", () => { config: configBlockNoEmpty, }).then(() => { throw new Error("should not have succeeded") - }).catch((actualError) => { + }).catch(actualError => { expect(actualError).toEqual(expectedError) }) }) @@ -100,7 +102,7 @@ it("standalone with non-existent-file and allowEmptyInput false should throw err allowEmptyInput: false, }).then(() => { throw new Error("should not have succeeded") - }).catch((actualError) => { + }).catch(actualError => { expect(actualError).toEqual(expectedError) }) }) @@ -110,11 +112,11 @@ it("standalone with non-existent-file and allowEmptyInput true", () => { files: `${fixturesPath}/non-existent-file.css`, config: configBlockNoEmpty, allowEmptyInput: true, - }).then(({ output, results, errored }) => { - expect(typeof output).toBe("string") - expect(results.length).toBe(0) - expect(errored).toBe(false) - expect(output).toBe("[]") + }).then((linted) => { + expect(typeof linted.output).toBe("string") + expect(linted.results.length).toBe(0) + expect(linted.errored).toBe(false) + expect(linted.output).toBe("[]") }) }) @@ -163,8 +165,8 @@ it("standalone passing file with syntax error", () => { code: "a { color: 'red; }", codeFilename: path.join(__dirname, "syntax-error.css"), config: { rules: { "block-no-empty": true } }, - }).then(({ results }) => { - expect(results[0].source.indexOf("syntax-error.css")).not.toBe(-1) + }).then((linted) => { + expect(linted.results[0].source.indexOf("syntax-error.css")).not.toBe(-1) }) }) @@ -172,8 +174,8 @@ it("syntax error sets errored to true", () => { return standalone({ code: "a { color: 'red; }", config: { rules: { "block-no-empty": true } }, - }).then(({ errored }) => { - expect(errored).toBe(true) + }).then((linted) => { + expect(linted.errored).toBe(true) }) }) @@ -181,8 +183,8 @@ describe("configuration error sets errored to true", () => { return standalone({ code: "a { color: 'red'; }", config: { rules: { "block-no-empty": "wahoo" } }, - }).then(({ errored }) => { - expect(errored).toBe(true) + }).then((linted) => { + expect(linted.errored).toBe(true) }) }) @@ -228,7 +230,7 @@ describe("standalone with deprecations", () => { beforeEach(() => { ruleDefinitionsStub = sinon.stub(ruleDefinitions, "block-no-empty", () => { return (root, result) => { - result.warn(("Some deprecation"), { + result.warn("Some deprecation", { stylelintType: "deprecation", }) } @@ -265,27 +267,27 @@ it("standalone with different configs per file", () => { }) it("no warnings for A", () => { - const resultA = results.find((result) => result.source.indexOf("a.css") !== -1) + const resultA = results.find(result => result.source.indexOf("a.css") !== -1) expect(resultA.warnings.length, 0) }) it("one warning for B", () => { - const resultB = results.find((result) => result.source.indexOf("b.css") !== -1) + const resultB = results.find(result => result.source.indexOf("b.css") !== -1) expect(resultB.warnings.length, 1) }) it("correct warning for B", () => { - const resultB = results.find((result) => result.source.indexOf("b.css") !== -1) + const resultB = results.find(result => result.source.indexOf("b.css") !== -1) expect(resultB.warnings[0].text.indexOf("Unexpected empty block")).not.toBe(-1) }) it("no warnings for C", () => { - const resultC = results.find((result) => result.source.indexOf("c.css") !== -1) + const resultC = results.find(result => result.source.indexOf("c.css") !== -1) expect(resultC.warnings.length, 0) }) it("no warnings for D", () => { - const resultD = results.find((result) => result.source.indexOf("d.css") !== -1) + const resultD = results.find(result => result.source.indexOf("d.css") !== -1) expect(resultD.warnings.length, 0) }) }) @@ -334,9 +336,9 @@ it("Setting `plugins` inside `configOverrides` object should overrides the ones configOverrides: { plugins: ["./fixtures/plugin-warn-about-bar"], }, - }).then(({ results }) => { - expect(results[0].warnings.length).toBe(1) - expect(results[0].warnings[0].text).toBe("found .bar (plugin/warn-about-bar)") + }).then((linted) => { + expect(linted.results[0].warnings.length).toBe(1) + expect(linted.results[0].warnings[0].text).toBe("found .bar (plugin/warn-about-bar)") }) }) @@ -363,8 +365,8 @@ describe("nonexistent codeFilename with loaded config", () => { return standalone({ code: "a {}", codeFilename: "does-not-exist.css", - }).then(({ results }) => { - expect(results[0].warnings.length).toBe(1) + }).then((linted) => { + expect(linted.results[0].warnings.length).toBe(1) }) }) }) diff --git a/src/__tests__/stylelintignore-test/.stylelintignore b/lib/__tests__/stylelintignore-test/.stylelintignore similarity index 100% rename from src/__tests__/stylelintignore-test/.stylelintignore rename to lib/__tests__/stylelintignore-test/.stylelintignore diff --git a/src/__tests__/stylelintignore-test/stylelintignore.test.js b/lib/__tests__/stylelintignore-test/stylelintignore.test.js similarity index 88% rename from src/__tests__/stylelintignore-test/stylelintignore.test.js rename to lib/__tests__/stylelintignore-test/stylelintignore.test.js index 8f028ae7e2..7bd53f3432 100644 --- a/src/__tests__/stylelintignore-test/stylelintignore.test.js +++ b/lib/__tests__/stylelintignore-test/stylelintignore.test.js @@ -1,5 +1,7 @@ -import path from "path" -import standalone from "../../standalone" +"use strict" + +const path = require("path") +const standalone = require("../../standalone") describe("standalone with .stylelintignore file ignoring one file", () => { let actualCwd @@ -19,10 +21,7 @@ describe("standalone with .stylelintignore file ignoring one file", () => { return standalone({ files: [ `${fixturesPath}/empty-block.css`, `${fixturesPath}/invalid-hex.css` ], config: { - extends: [ - `${fixturesPath}/config-block-no-empty`, - `${fixturesPath}/config-color-no-invalid-hex`, - ], + extends: [ `${fixturesPath}/config-block-no-empty`, `${fixturesPath}/config-color-no-invalid-hex` ], }, }).then(data => results = data.results) }) diff --git a/src/assignDisabledRanges.js b/lib/assignDisabledRanges.js similarity index 79% rename from src/assignDisabledRanges.js rename to lib/assignDisabledRanges.js index 8239ffcf32..e284aca284 100644 --- a/src/assignDisabledRanges.js +++ b/lib/assignDisabledRanges.js @@ -1,5 +1,6 @@ /* @flow */ -import _ from "lodash" +"use strict" +const _ = require("lodash") const COMMAND_PREFIX = "stylelint-" const disableCommand = COMMAND_PREFIX + "disable" @@ -8,20 +9,19 @@ const disableLineCommand = COMMAND_PREFIX + "disable-line" const disableNextLineCommand = COMMAND_PREFIX + "disable-next-line" const ALL_RULES = "all" -type disabledRangeObject = { +// Run it like a plugin ... +/*:: type disabledRangeObject = { [ruleName: string]: Array<{ start: number, end?: number, }> -} - -// Run it like a plugin ... -export default function (root: Object, result: Object) { +}*/ +module.exports = function (root/*: Object*/, result/*: Object*/) { result.stylelint = result.stylelint || {} // Most of the functions below work via side effects mutating // this object - const disabledRanges: disabledRangeObject = { + const disabledRanges/*: disabledRangeObject*/ = { all: [], } result.stylelint.disabledRanges = disabledRanges @@ -29,23 +29,19 @@ export default function (root: Object, result: Object) { return result - function processDisableLineCommand(comment: postcss$comment) { + function processDisableLineCommand(comment/*: postcss$comment*/) { getCommandRules(disableLineCommand, comment.text).forEach(ruleName => { disableLine(comment.source.start.line, ruleName, comment) }) } - function processDisableNextLineCommand(comment: postcss$comment) { + function processDisableNextLineCommand(comment/*: postcss$comment*/) { getCommandRules(disableNextLineCommand, comment.text).forEach(ruleName => { disableLine(comment.source.start.line + 1, ruleName, comment) }) } - function disableLine( - line: number, - ruleName: string, - comment: postcss$comment - ) { + function disableLine(line/*: number*/, ruleName/*: string*/, comment/*: postcss$comment*/) { if (ruleIsDisabled(ALL_RULES)) { throw comment.error("All rules have already been disabled", { plugin: "stylelint" }) } @@ -63,7 +59,7 @@ export default function (root: Object, result: Object) { } } - function processDisableCommand(comment: postcss$comment) { + function processDisableCommand(comment/*: postcss$comment*/) { getCommandRules(disableCommand, comment.text).forEach(ruleToDisable => { if (ruleToDisable === ALL_RULES) { if (ruleIsDisabled(ALL_RULES)) { @@ -82,7 +78,7 @@ export default function (root: Object, result: Object) { }) } - function processEnableCommand(comment: postcss$comment) { + function processEnableCommand(comment/*: postcss$comment*/) { getCommandRules(enableCommand, comment.text).forEach(ruleToEnable => { if (ruleToEnable === ALL_RULES) { if (_.values(disabledRanges).every(ranges => _.isEmpty(ranges) || !!_.last(ranges.end))) { @@ -116,11 +112,14 @@ export default function (root: Object, result: Object) { }) } - function checkComment(comment: postcss$comment) { - const { text } = comment + function checkComment(comment/*: postcss$comment*/) { + const text = comment.text // Ignore comments that are not relevant commands - if (text.indexOf(COMMAND_PREFIX) !== 0) { return result } + + if (text.indexOf(COMMAND_PREFIX) !== 0) { + return result + } if (text.indexOf(disableLineCommand) === 0) { processDisableLineCommand(comment) @@ -133,35 +132,36 @@ export default function (root: Object, result: Object) { } } - function getCommandRules( - command: string, - fullText: string - ): Array { + function getCommandRules(command/*: string*/, fullText/*: string*/)/*: Array*/ { const rules = _.compact(fullText.slice(command.length).split(",")).map(r => r.trim()) - if (_.isEmpty(rules)) { return [ALL_RULES] } + if (_.isEmpty(rules)) { + return [ALL_RULES] + } return rules } - function startDisabledRange(line: number, ruleName: string) { + function startDisabledRange(line/*: number*/, ruleName/*: string*/) { const rangeObj = { start: line } ensureRuleRanges(ruleName) disabledRanges[ruleName].push(rangeObj) } - function endDisabledRange(line: number, ruleName: string) { + function endDisabledRange(line/*: number*/, ruleName/*: string*/) { const lastRangeForRule = _.last(disabledRanges[ruleName]) - if (!lastRangeForRule) { return } + if (!lastRangeForRule) { + return + } // Add an `end` prop to the last range of that rule lastRangeForRule.end = line } - function ensureRuleRanges(ruleName: string) { + function ensureRuleRanges(ruleName/*: string*/) { if (!disabledRanges[ruleName]) { disabledRanges[ruleName] = _.cloneDeep(disabledRanges.all) } } - function ruleIsDisabled(ruleName: string): boolean { + function ruleIsDisabled(ruleName/*: string*/)/*: boolean*/ { if (disabledRanges[ruleName] === undefined) return false if (_.last(disabledRanges[ruleName]) === undefined) return false if (_.get(_.last(disabledRanges[ruleName]), "end") === undefined) return true diff --git a/src/augmentConfig.js b/lib/augmentConfig.js similarity index 51% rename from src/augmentConfig.js rename to lib/augmentConfig.js index 5a9f1fd3c2..373bee6163 100644 --- a/src/augmentConfig.js +++ b/lib/augmentConfig.js @@ -1,11 +1,13 @@ /* @flow */ -import { configurationError, getModulePath } from "./utils" -import _ from "lodash" -import fs from "fs" -import globjoin from "globjoin" -import normalizeRuleSettings from "./normalizeRuleSettings" -import path from "path" -import rules from "./rules" +"use strict" +const configurationError = require("./utils/configurationError") +const getModulePath = require("./utils/getModulePath") +const _ = require("lodash") +const fs = require("fs") +const globjoin = require("globjoin") +const normalizeRuleSettings = require("./normalizeRuleSettings") +const path = require("path") +const rules = require("./rules") const DEFAULT_IGNORE_FILENAME = ".stylelintignore" const FILE_NOT_FOUND_ERROR_CODE = "ENOENT" @@ -13,42 +15,32 @@ const FILE_NOT_FOUND_ERROR_CODE = "ENOENT" // - Merges config and configOverrides // - Makes all paths absolute // - Merges extends -function augmentConfigBasic( - stylelint: stylelint$internalApi, - config: stylelint$config, - configDir: string, -): Promise { - return Promise.resolve() - .then(() => { - return _.merge(config, stylelint._options.configOverrides) - }) - .then((augmentedConfig) => { - return extendConfig(stylelint, augmentedConfig, configDir) - }) - .then((augmentedConfig) => { - return absolutizePaths(augmentedConfig, configDir) - }) +function augmentConfigBasic(stylelint/*: stylelint$internalApi*/, config/*: stylelint$config*/, configDir/*: string*/)/*: Promise*/ { + return Promise.resolve().then(() => { + return _.merge(config, stylelint._options.configOverrides) + }).then(augmentedConfig => { + return extendConfig(stylelint, augmentedConfig, configDir) + }).then(augmentedConfig => { + return absolutizePaths(augmentedConfig, configDir) + }) } // Extended configs need to be run through augmentConfigBasic // but do not need the full treatment. Things like pluginFunctions // will be resolved and added by the parent config. -function augmentConfigExtended( - stylelint: stylelint$internalApi, - cosmiconfigResultArg: ?{ - config: stylelint$config, - filepath: string, - }, -): Promise { +function augmentConfigExtended(stylelint/*: stylelint$internalApi*/, cosmiconfigResultArg/*: ?{ + config: stylelint$config, + filepath: string, + }*/)/*: Promise*/ { const cosmiconfigResult = cosmiconfigResultArg // Lock in for Flow if (!cosmiconfigResult) return Promise.resolve(null) const configDir = path.dirname(cosmiconfigResult.filepath || "") const cleanedConfig = _.omit(cosmiconfigResult.config, "ignoreFiles") - return augmentConfigBasic(stylelint, cleanedConfig, configDir).then((augmentedConfig) => { + return augmentConfigBasic(stylelint, cleanedConfig, configDir).then(augmentedConfig => { return { config: augmentedConfig, filepath: cosmiconfigResult.filepath, @@ -56,71 +48,60 @@ function augmentConfigExtended( }) } -function augmentConfigFull( - stylelint: stylelint$internalApi, - cosmiconfigResultArg: ?{ - config: stylelint$config, - filepath: string, - }, -): Promise { +function augmentConfigFull(stylelint/*: stylelint$internalApi*/, cosmiconfigResultArg/*: ?{ + config: stylelint$config, + filepath: string, + }*/)/*: Promise*/ { const cosmiconfigResult = cosmiconfigResultArg // Lock in for Flow if (!cosmiconfigResult) return Promise.resolve(null) - const { config, filepath } = cosmiconfigResult + const config = cosmiconfigResult.config, + filepath = cosmiconfigResult.filepath - const configDir = stylelint._options.configBasedir - || path.dirname(filepath || "") + const configDir = stylelint._options.configBasedir || path.dirname(filepath || "") - return augmentConfigBasic(stylelint, config, configDir) - .then((augmentedConfig) => { - return addIgnorePatterns(stylelint, augmentedConfig) - }) - .then((augmentedConfig) => { - return addPluginFunctions(augmentedConfig) - }) - .then((augmentedConfig) => { - return addProcessorFunctions(augmentedConfig) - }) - .then((augmentedConfig) => { - if (!augmentedConfig.rules) { - throw configurationError("No rules found within configuration. Have you provided a \"rules\" property?") - } + return augmentConfigBasic(stylelint, config, configDir).then(augmentedConfig => { + return addIgnorePatterns(stylelint, augmentedConfig) + }).then(augmentedConfig => { + return addPluginFunctions(augmentedConfig) + }).then(augmentedConfig => { + return addProcessorFunctions(augmentedConfig) + }).then(augmentedConfig => { + if (!augmentedConfig.rules) { + throw configurationError("No rules found within configuration. Have you provided a \"rules\" property?") + } - return normalizeAllRuleSettings(augmentedConfig) - }) - .then((augmentedConfig) => { - return { - config: augmentedConfig, - filepath: cosmiconfigResult.filepath, - } - }) + return normalizeAllRuleSettings(augmentedConfig) + }).then(augmentedConfig => { + return { + config: augmentedConfig, + filepath: cosmiconfigResult.filepath, + } + }) } // Load a file ignore ignore patterns, if there is one; // then add them to the config as an ignorePatterns property -function addIgnorePatterns( - stylelint: stylelint$internalApi, - config: stylelint$config, -): Promise { +function addIgnorePatterns(stylelint/*: stylelint$internalApi*/, config/*: stylelint$config*/)/*: Promise*/ { const ignoreFilePath = stylelint._options.ignorePath || DEFAULT_IGNORE_FILENAME - const absoluteIgnoreFilePath = (path.isAbsolute(ignoreFilePath)) - ? ignoreFilePath - : path.resolve(process.cwd(), ignoreFilePath) + const absoluteIgnoreFilePath = path.isAbsolute(ignoreFilePath) ? ignoreFilePath : path.resolve(process.cwd(), ignoreFilePath) return new Promise((resolve, reject) => { fs.readFile(absoluteIgnoreFilePath, "utf8", (err, data) => { if (err) { // If the file's not found, fine, we'll just // consider it an empty array of globs - if (err.code === FILE_NOT_FOUND_ERROR_CODE) { return resolve(config) } + if (err.code === FILE_NOT_FOUND_ERROR_CODE) { + return resolve(config) + } return reject(err) } // Add an ignorePatterns property to the config, containing the // .gitignore-patterned globs loaded from .stylelintignore - const augmentedConfig: stylelint$config = Object.assign({}, config, { + const augmentedConfig/*: stylelint$config*/ = Object.assign({}, config, { ignorePatterns: data, }) resolve(augmentedConfig) @@ -133,12 +114,9 @@ function addIgnorePatterns( // - plugins // - processors // (extends handled elsewhere) -function absolutizePaths( - config: stylelint$config, - configDir: string, -): stylelint$config { +function absolutizePaths(config/*: stylelint$config*/, configDir/*: string*/)/*: stylelint$config*/ { if (config.ignoreFiles) { - config.ignoreFiles = [].concat(config.ignoreFiles).map((glob) => { + config.ignoreFiles = [].concat(config.ignoreFiles).map(glob => { if (path.isAbsolute(glob.replace(/^!/, ""))) return glob return globjoin(configDir, glob) }) @@ -159,60 +137,41 @@ function absolutizePaths( // Processors are absolutized in their own way because // they can be and return a string or an array -function absolutizeProcessors( - processors: stylelint$configProcessors, - configDir: string, -): stylelint$configProcessors { - const normalizedProcessors = (Array.isArray(processors)) - ? processors - : [processors] - - return normalizedProcessors.map((item) => { +function absolutizeProcessors(processors/*: stylelint$configProcessors*/, configDir/*: string*/)/*: stylelint$configProcessors*/ { + const normalizedProcessors = Array.isArray(processors) ? processors : [processors] + + return normalizedProcessors.map(item => { if (typeof item === "string") { return getModulePath(configDir, item) } - return [ - getModulePath(configDir, item[0]), - item[1], - ] + return [ getModulePath(configDir, item[0]), item[1] ] }) } -function extendConfig( - stylelint: stylelint$internalApi, - config: stylelint$config, - configDir: string, -): Promise { +function extendConfig(stylelint/*: stylelint$internalApi*/, config/*: stylelint$config*/, configDir/*: string*/)/*: Promise*/ { if (config.extends === undefined) return Promise.resolve(config) - const normalizedExtends = (Array.isArray(config.extends)) - ? config.extends - : [config.extends] + const normalizedExtends = Array.isArray(config.extends) ? config.extends : [config.extends] const originalWithoutExtends = _.omit(config, "extends") const loadExtends = normalizedExtends.reduce((resultPromise, extendLookup) => { - return resultPromise.then((resultConfig) => { - return loadExtendedConfig(stylelint, resultConfig, configDir, extendLookup).then((extendResult) => { + return resultPromise.then(resultConfig => { + return loadExtendedConfig(stylelint, resultConfig, configDir, extendLookup).then(extendResult => { if (!extendResult) return resultConfig return mergeConfigs(resultConfig, extendResult.config) }) }) }, Promise.resolve(originalWithoutExtends)) - return loadExtends.then((resultConfig) => { + return loadExtends.then(resultConfig => { return mergeConfigs(resultConfig, originalWithoutExtends) }) } -function loadExtendedConfig( - stylelint: stylelint$internalApi, - config: stylelint$config, - configDir: string, - extendLookup: string, -): Promise { +function loadExtendedConfig(stylelint/*: stylelint$internalApi*/, config/*: stylelint$config*/, configDir/*: string*/, extendLookup/*: string*/)/*: Promise*/ { const extendPath = getModulePath(configDir, extendLookup) return stylelint._extendExplorer.load(null, extendPath) } @@ -223,10 +182,7 @@ function loadExtendedConfig( // merge any given rule's settings. If b contains the same rule as a, // b's rule settings will override a's rule settings entirely. // - Everything else is merged via Object.assign -function mergeConfigs( - a: stylelint$config, - b: stylelint$config, -): stylelint$config { +function mergeConfigs(a/*: stylelint$config*/, b/*: stylelint$config*/)/*: stylelint$config*/ { const pluginMerger = {} if (a.plugins || b.plugins) { pluginMerger.plugins = [] @@ -258,14 +214,10 @@ function mergeConfigs( return result } -function addPluginFunctions( - config: stylelint$config, -): stylelint$config { +function addPluginFunctions(config/*: stylelint$config*/)/*: stylelint$config*/ { if (!config.plugins) return config - const normalizedPlugins = (Array.isArray(config.plugins)) - ? config.plugins - : [config.plugins] + const normalizedPlugins = Array.isArray(config.plugins) ? config.plugins : [config.plugins] const pluginFunctions = normalizedPlugins.reduce((result, pluginLookup) => { let pluginImport = require(pluginLookup) @@ -274,26 +226,15 @@ function addPluginFunctions( // A plugin can export either a single rule definition // or an array of them - const normalizedPluginImport = (Array.isArray(pluginImport)) - ? pluginImport - : [pluginImport] + const normalizedPluginImport = Array.isArray(pluginImport) ? pluginImport : [pluginImport] - normalizedPluginImport.forEach((pluginRuleDefinition) => { + normalizedPluginImport.forEach(pluginRuleDefinition => { if (!pluginRuleDefinition.ruleName) { - throw configurationError( - "stylelint v3+ requires plugins to expose a ruleName. " + - `The plugin "${pluginLookup}" is not doing this, so will not work ` + - "with stylelint v3+. Please file an issue with the plugin." - ) + throw configurationError("stylelint v3+ requires plugins to expose a ruleName. " + `The plugin "${pluginLookup}" is not doing this, so will not work ` + "with stylelint v3+. Please file an issue with the plugin.") } if (!_.includes(pluginRuleDefinition.ruleName, "/")) { - throw configurationError( - "stylelint v7+ requires plugin rules to be namspaced, " + - "i.e. only `plugin-namespace/plugin-rule-name` plugin rule names are supported. " + - `The plugin rule "${pluginRuleDefinition.ruleName}" does not do this, so will not work. ` + - "Please file an issue with the plugin." - ) + throw configurationError("stylelint v7+ requires plugin rules to be namspaced, " + "i.e. only `plugin-namespace/plugin-rule-name` plugin rule names are supported. " + `The plugin rule "${pluginRuleDefinition.ruleName}" does not do this, so will not work. ` + "Please file an issue with the plugin.") } result[pluginRuleDefinition.ruleName] = pluginRuleDefinition.rule @@ -306,12 +247,10 @@ function addPluginFunctions( return config } -function normalizeAllRuleSettings( - config: stylelint$config, -): stylelint$config { +function normalizeAllRuleSettings(config/*: stylelint$config*/)/*: stylelint$config*/ { const normalizedRules = {} if (!config.rules) return config - Object.keys(config.rules).forEach((ruleName) => { + Object.keys(config.rules).forEach(ruleName => { const rawRuleSettings = _.get(config, [ "rules", ruleName ]) const rule = rules[ruleName] || _.get(config, [ "pluginFunctions", ruleName ]) if (!rule) { @@ -334,15 +273,11 @@ function normalizeAllRuleSettings( // provided options // - Push the processor's code and result processors to their respective arrays const processorCache = new Map() -function addProcessorFunctions( - config: stylelint$config -): stylelint$config { +function addProcessorFunctions(config/*: stylelint$config*/)/*: stylelint$config*/ { if (!config.processors) return config const codeProcessors = [] - const resultProcessors = [] - - ;[].concat(config.processors).forEach((processorConfig) => { + const resultProcessors = [];[].concat(config.processors).forEach(processorConfig => { const processorKey = JSON.stringify(processorConfig) let initializedProcessor @@ -371,7 +306,4 @@ function addProcessorFunctions( return config } -export { - augmentConfigExtended, - augmentConfigFull, -} +module.exports = { augmentConfigExtended, augmentConfigFull } diff --git a/src/cli.js b/lib/cli.js similarity index 80% rename from src/cli.js rename to lib/cli.js index 34c010e531..2a1f41b12b 100644 --- a/src/cli.js +++ b/lib/cli.js @@ -1,13 +1,13 @@ #!/usr/bin/env node /* @flow */ -import { assign } from "lodash" -import { getModulePath } from "./utils" -import getStdin from "get-stdin" -import meow from "meow" -import needlessDisablesStringFormatter from "./formatters/needlessDisablesStringFormatter" -import path from "path" -import resolveFrom from "resolve-from" -import standalone from "./standalone" +"use strict" +const getModulePath = require("./utils/getModulePath") +const getStdin = require("get-stdin") +const meow = require("meow") +const needlessDisablesStringFormatter = require("./formatters/needlessDisablesStringFormatter") +const path = require("path") +const resolveFrom = require("resolve-from") +const standalone = require("./standalone") const minimistOptions = { default: { @@ -123,13 +123,11 @@ const cli = meow(meowOptions, minimistOptions) let formatter = cli.flags.formatter if (cli.flags.customFormatter) { - const customFormatter = path.isAbsolute(cli.flags.customFormatter) - ? cli.flags.customFormatter - : path.join(process.cwd(), cli.flags.customFormatter) + const customFormatter = path.isAbsolute(cli.flags.customFormatter) ? cli.flags.customFormatter : path.join(process.cwd(), cli.flags.customFormatter) formatter = require(customFormatter) } -const optionsBase: Object = { +const optionsBase/*: Object*/ = { formatter, configOverrides: {}, } @@ -153,14 +151,11 @@ if (cli.flags.config) { // c. relative path relative to `process.cwd()`. // If none of the above work, we'll try a relative path starting // in `process.cwd()`. - optionsBase.configFile = resolveFrom(process.cwd(), cli.flags.config) - || path.join(process.cwd(), cli.flags.config) + optionsBase.configFile = resolveFrom(process.cwd(), cli.flags.config) || path.join(process.cwd(), cli.flags.config) } if (cli.flags.configBasedir) { - optionsBase.configBasedir = (path.isAbsolute(cli.flags.configBasedir)) - ? cli.flags.configBasedir - : path.resolve(process.cwd(), cli.flags.configBasedir) + optionsBase.configBasedir = path.isAbsolute(cli.flags.configBasedir) ? cli.flags.configBasedir : path.resolve(process.cwd(), cli.flags.configBasedir) } if (cli.flags.stdinFilename) { @@ -179,7 +174,8 @@ if (cli.flags.allowEmptyInput) { optionsBase.allowEmptyInput = cli.flags.allowEmptyInput } -const { reportNeedlessDisables } = cli.flags +const reportNeedlessDisables = cli.flags.reportNeedlessDisables + if (reportNeedlessDisables) { optionsBase.reportNeedlessDisables = reportNeedlessDisables } @@ -187,11 +183,11 @@ if (reportNeedlessDisables) { Promise.resolve().then(() => { // Add input/code into options if (cli.input.length) { - return assign({}, optionsBase, { + return Object.assign({}, optionsBase, { files: cli.input, }) } - return getStdin().then(stdin => assign({}, optionsBase, { + return getStdin().then(stdin => Object.assign({}, optionsBase, { code: stdin, })) }).then(options => { @@ -200,16 +196,22 @@ Promise.resolve().then(() => { } return standalone(options) -}).then(({ output, errored, needlessDisables }) => { +}).then((linted) => { if (reportNeedlessDisables) { - process.stdout.write(needlessDisablesStringFormatter(needlessDisables)) - if (reportNeedlessDisables === "error") { process.exitCode = 2 } + process.stdout.write(needlessDisablesStringFormatter(linted.needlessDisables)) + if (reportNeedlessDisables === "error") { + process.exitCode = 2 + } return } - if (!output) { return } - process.stdout.write(output) - if (errored) { process.exitCode = 2 } + if (!linted.output) { + return + } + process.stdout.write(linted.output) + if (linted.errored) { + process.exitCode = 2 + } }).catch(err => { console.log(err.stack) // eslint-disable-line no-console process.exit(err.code || 1) diff --git a/lib/createPlugin.js b/lib/createPlugin.js new file mode 100644 index 0000000000..3d710ccd05 --- /dev/null +++ b/lib/createPlugin.js @@ -0,0 +1,8 @@ +"use strict" + +module.exports = function (ruleName, rule) { + return { + ruleName, + rule, + } +} diff --git a/src/createStylelint.js b/lib/createStylelint.js similarity index 54% rename from src/createStylelint.js rename to lib/createStylelint.js index 41dfc7fdf1..a20efb0e8e 100644 --- a/src/createStylelint.js +++ b/lib/createStylelint.js @@ -1,34 +1,32 @@ /* @flow */ -import { - augmentConfigExtended, - augmentConfigFull, -} from "./augmentConfig" -import _ from "lodash" -import cosmiconfig from "cosmiconfig" -import createStylelintResult from "./createStylelintResult" -import getConfigForFile from "./getConfigForFile" -import getPostcssResult from "./getPostcssResult" -import isPathIgnored from "./isPathIgnored" -import lintSource from "./lintSource" +"use strict" +const augmentConfig = require("./augmentConfig") +const _ = require("lodash") +const cosmiconfig = require("cosmiconfig") +const createStylelintResult = require("./createStylelintResult") +const getConfigForFile = require("./getConfigForFile") +const getPostcssResult = require("./getPostcssResult") +const isPathIgnored = require("./isPathIgnored") +const lintSource = require("./lintSource") // The stylelint "internal API" is passed among functions // so that methods on a stylelint instance can invoke // each other while sharing options and caches -export default function ( - options: stylelint$options = {}, -): stylelint$internalApi { - const stylelint: Object = { _options: options } +module.exports = function ()/*: stylelint$internalApi*/ { + const options/*: stylelint$options*/ = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {} + + const stylelint/*: Object*/ = { _options: options } // Two separate explorers so they can each have their own transform // function whose results are cached by cosmiconfig stylelint._fullExplorer = cosmiconfig("stylelint", { argv: false, rcExtensions: true, - transform: _.partial(augmentConfigFull, stylelint), + transform: _.partial(augmentConfig.augmentConfigFull, stylelint), }) stylelint._extendExplorer = cosmiconfig(null, { argv: false, - transform: _.partial(augmentConfigExtended, stylelint), + transform: _.partial(augmentConfig.augmentConfigExtended, stylelint), }) stylelint._specifiedConfigCache = new Map() diff --git a/src/createStylelintResult.js b/lib/createStylelintResult.js similarity index 61% rename from src/createStylelintResult.js rename to lib/createStylelintResult.js index 3f78f81f9f..9126346cbb 100644 --- a/src/createStylelintResult.js +++ b/lib/createStylelintResult.js @@ -1,18 +1,13 @@ /* @flow */ -import _ from "lodash" +"use strict" +const _ = require("lodash") -export default function ( - stylelint: stylelint$internalApi, - postcssResult: Object, - filePath?: string, -): Promise { - const source = (!postcssResult.root.source) - ? undefined - : postcssResult.root.source.input.file || postcssResult.root.source.input.id +module.exports = function (stylelint/*: stylelint$internalApi*/, postcssResult/*: Object*/, filePath/*:: ?: string*/)/*: Promise*/ { + const source = !postcssResult.root.source ? undefined : postcssResult.root.source.input.file || postcssResult.root.source.input.id // Strip out deprecation warnings from the messages const deprecationMessages = _.remove(postcssResult.messages, { stylelintType: "deprecation" }) - const deprecations = deprecationMessages.map((deprecationMessage) => { + const deprecations = deprecationMessages.map(deprecationMessage => { return { text: deprecationMessage.text, reference: deprecationMessage.stylelintReference, @@ -21,7 +16,7 @@ export default function ( // Also strip out invalid options const invalidOptionMessages = _.remove(postcssResult.messages, { stylelintType: "invalidOption" }) - const invalidOptionWarnings = invalidOptionMessages.map((invalidOptionMessage) => { + const invalidOptionWarnings = invalidOptionMessages.map(invalidOptionMessage => { return { text: invalidOptionMessage.text, } @@ -33,7 +28,7 @@ export default function ( deprecations, invalidOptionWarnings, errored: postcssResult.stylelint.stylelintError, - warnings: postcssResult.messages.map((message) => { + warnings: postcssResult.messages.map(message => { return { line: message.line, column: message.column, @@ -46,13 +41,17 @@ export default function ( _postcssResult: postcssResult, } - return stylelint.getConfigForFile(filePath).then(({ config }) => { + return stylelint.getConfigForFile(filePath).then((result) => { + const config = result.config + if (config.resultProcessors) { - config.resultProcessors.forEach((resultProcessor) => { + config.resultProcessors.forEach(resultProcessor => { // Result processors might just mutate the result object, // or might return a new one const returned = resultProcessor(stylelintResult, source) - if (returned) { stylelintResult = returned } + if (returned) { + stylelintResult = returned + } }) } diff --git a/src/formatters/__tests__/jsonFormatter-test.js b/lib/formatters/__tests__/jsonFormatter-test.js similarity index 62% rename from src/formatters/__tests__/jsonFormatter-test.js rename to lib/formatters/__tests__/jsonFormatter-test.js index 13acfdfd82..cb00f73892 100644 --- a/src/formatters/__tests__/jsonFormatter-test.js +++ b/lib/formatters/__tests__/jsonFormatter-test.js @@ -1,11 +1,13 @@ -import jsonFormatter from "../jsonFormatter" -import test from "tape" +"use strict" + +const jsonFormatter = require("../jsonFormatter") +const test = require("tape") test("json formatter", t => { const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": true, - "warnings":[{ + "warnings": [{ "line": 1, "column": 2, "rule": "bar", @@ -13,7 +15,7 @@ test("json formatter", t => { "text": "Unexpected foo", }], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] t.deepEqual(JSON.parse(jsonFormatter(results)), results) diff --git a/lib/formatters/__tests__/needlessDisablesStringFormatter-test.js b/lib/formatters/__tests__/needlessDisablesStringFormatter-test.js new file mode 100644 index 0000000000..1b812b6039 --- /dev/null +++ b/lib/formatters/__tests__/needlessDisablesStringFormatter-test.js @@ -0,0 +1,32 @@ +"use strict" + +const chalk = require("chalk") +const needlessDisablesStringFormatter = require("../needlessDisablesStringFormatter") +const stripIndent = require("common-tags").stripIndent +const test = require("tape") + +test("needlessDisables formatter stringified", t => { + const actual = chalk.stripColor(needlessDisablesStringFormatter([ { + source: "foo", + ranges: [ { start: 1, end: 3 }, { start: 7 } ], + }, { + source: "bar", + ranges: [ { start: 19, end: 33 }, { start: 99, end: 102 } ], + }, { + sourc: "baz", + ranges: [], + } ])) + + let expected = stripIndent` + foo + start: 1, end: 3 + start: 7 + + bar + start: 19, end: 33 + start: 99, end: 102` + expected = `\n${expected}\n` + + t.equal(actual, expected) + t.end() +}) diff --git a/lib/formatters/__tests__/prepareFormatterOutput.js b/lib/formatters/__tests__/prepareFormatterOutput.js new file mode 100644 index 0000000000..1941f7016a --- /dev/null +++ b/lib/formatters/__tests__/prepareFormatterOutput.js @@ -0,0 +1,19 @@ +"use strict" + +const chalk = require("chalk") + +const symbolConversions = new Map() +symbolConversions.set("ℹ", "i") +symbolConversions.set("✔", "√") +symbolConversions.set("⚠", "‼") +symbolConversions.set("✖", "×") + +module.exports = function (results, formatter) { + let output = chalk.stripColor(formatter(results)).trim() + + symbolConversions.forEach((win, nix) => { + output = output.replace(new RegExp(nix, "g"), win) + }) + + return output +} diff --git a/src/formatters/__tests__/stringFormatter-test.js b/lib/formatters/__tests__/stringFormatter-test.js similarity index 76% rename from src/formatters/__tests__/stringFormatter-test.js rename to lib/formatters/__tests__/stringFormatter-test.js index f57621f23f..c075341a1f 100644 --- a/src/formatters/__tests__/stringFormatter-test.js +++ b/lib/formatters/__tests__/stringFormatter-test.js @@ -1,21 +1,17 @@ -import chalk from "chalk" -import stringFormatter from "../stringFormatter" -import { stripIndent } from "common-tags" -import test from "tape" +"use strict" -const symbolConversions = new Map() -symbolConversions.set("ℹ", "i") -symbolConversions.set("✔", "√") -symbolConversions.set("⚠", "‼") -symbolConversions.set("✖", "×") +const stringFormatter = require("../stringFormatter") +const stripIndent = require("common-tags").stripIndent +const test = require("tape") +const prepareFormatterOutput = require("./prepareFormatterOutput") test("no warnings", t => { const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": false, "warnings": [], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] const output = stringFormatter(results) @@ -27,7 +23,7 @@ test("no warnings", t => { test("warnings", t => { const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": true, "warnings": [{ "line": 1, @@ -37,7 +33,7 @@ test("warnings", t => { "text": "Unexpected foo", }], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] const output = prepareFormatterOutput(results, stringFormatter) @@ -55,7 +51,7 @@ test("warnings without stdout `TTY`", t => { process.stdout.isTTY = false const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": true, "warnings": [{ "line": 1, @@ -65,15 +61,14 @@ test("warnings without stdout `TTY`", t => { "text": "Unexpected foo", }], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] const output = prepareFormatterOutput(results, stringFormatter) t.equal(output, stripIndent` path/to/file.css - 1:1 × Unexpected foo bar` - ) + 1:1 × Unexpected foo bar`) process.stdout.isTTY = oldTTY @@ -88,7 +83,7 @@ test("warnings with more than 80 characters and `process.stdout.columns` equal 9 process.stdout.columns = 90 const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": true, "warnings": [{ "line": 1, @@ -98,19 +93,16 @@ test("warnings with more than 80 characters and `process.stdout.columns` equal 9 "text": "Unexpected very very very very very very very very very very very very very long foo", }], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] const output = prepareFormatterOutput(results, stringFormatter) - t.equal( - output, - stripIndent` + t.equal(output, stripIndent` path/to/file.css 1:1 × Unexpected very very very very very very very bar-very-very-very-very-very-long very very very very very very long foo - ` - ) + `) process.stdout.isTTY = oldTTY process.stdout.columns = stdoutColumn @@ -157,7 +149,7 @@ test("condensing of deprecations and invalid option warnings", t => { test("one ignored file", t => { const results = [{ "source": "file.css", - "warnings":[], + "warnings": [], "deprecations": [], "invalidOptionWarnings": [], "ignored": true, @@ -168,13 +160,3 @@ test("one ignored file", t => { t.equal(output, "") t.end() }) - -export function prepareFormatterOutput(results, formatter) { - let output = chalk.stripColor(formatter(results)).trim() - - for (const [ nix, win ] of symbolConversions.entries()) { - output = output.replace(new RegExp(nix, "g"), win) - } - - return output -} diff --git a/src/formatters/__tests__/verboseFormatter-test.js b/lib/formatters/__tests__/verboseFormatter-test.js similarity index 82% rename from src/formatters/__tests__/verboseFormatter-test.js rename to lib/formatters/__tests__/verboseFormatter-test.js index 22da7e4c3d..ef5f4935d5 100644 --- a/src/formatters/__tests__/verboseFormatter-test.js +++ b/lib/formatters/__tests__/verboseFormatter-test.js @@ -1,15 +1,17 @@ -import { prepareFormatterOutput } from "./stringFormatter-test" -import { stripIndent } from "common-tags" -import test from "tape" -import verboseFormatter from "../verboseFormatter" +"use strict" + +const prepareFormatterOutput = require("./prepareFormatterOutput") +const stripIndent = require("common-tags").stripIndent +const test = require("tape") +const verboseFormatter = require("../verboseFormatter") test("no warnings", t => { const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": false, "warnings": [], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] const output = prepareFormatterOutput(results, verboseFormatter) @@ -25,9 +27,9 @@ test("no warnings", t => { test("one warnings (of severity 'error')", t => { const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": true, - "warnings":[{ + "warnings": [{ "line": 1, "column": 2, "rule": "bar", @@ -35,7 +37,7 @@ test("one warnings (of severity 'error')", t => { "text": "Unexpected foo", }], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] const output = prepareFormatterOutput(results, verboseFormatter) @@ -59,9 +61,9 @@ test("0 stdout column", t => { process.stdout.columns = 0 const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": true, - "warnings":[{ + "warnings": [{ "line": 1, "column": 2, "rule": "bar", @@ -69,7 +71,7 @@ test("0 stdout column", t => { "text": "Unexpected foo", }], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] const output = prepareFormatterOutput(results, verboseFormatter) @@ -96,9 +98,9 @@ test("less than 80 stdout column", t => { process.stdout.columns = 79 const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": true, - "warnings":[{ + "warnings": [{ "line": 1, "column": 2, "rule": "bar", @@ -106,7 +108,7 @@ test("less than 80 stdout column", t => { "text": "Unexpected foo", }], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] const output = prepareFormatterOutput(results, verboseFormatter) @@ -130,9 +132,9 @@ test("less than 80 stdout column", t => { test("two of the same warnings of 'error' and one of 'warning' across two files", t => { const results = [ { - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": true, - "warnings":[ { + "warnings": [ { "line": 1, "column": 2, "rule": "bar", @@ -146,11 +148,11 @@ test("two of the same warnings of 'error' and one of 'warning' across two files" "text": "Unexpected foo", } ], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }, { - "source": "file2.css", + "source": "file2.css", "errored": true, - "warnings":[{ + "warnings": [{ "line": 3, "column": 1, "rule": "baz", @@ -158,7 +160,7 @@ test("two of the same warnings of 'error' and one of 'warning' across two files" "text": "Expected cat", }], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], } ] const output = prepareFormatterOutput(results, verboseFormatter) @@ -186,15 +188,15 @@ test("two of the same warnings of 'error' and one of 'warning' across two files" test("lineless syntax error", t => { const results = [{ - "source": "path/to/file.css", + "source": "path/to/file.css", "errored": false, - "warnings":[{ + "warnings": [{ "rule": "SyntaxError", "severity": "error", "text": "Unexpected foo", }], "deprecations": [], - "invalidOptionWarnings":[], + "invalidOptionWarnings": [], }] const output = prepareFormatterOutput(results, verboseFormatter) @@ -216,7 +218,7 @@ test("lineless syntax error", t => { test("one ignored file", t => { const results = [{ "source": "file.css", - "warnings":[], + "warnings": [], "deprecations": [], "invalidOptionWarnings": [], "ignored": true, diff --git a/lib/formatters/index.js b/lib/formatters/index.js new file mode 100644 index 0000000000..bc72b0ee38 --- /dev/null +++ b/lib/formatters/index.js @@ -0,0 +1,7 @@ +"use strict" + +module.exports = { + json: require("./jsonFormatter"), + string: require("./stringFormatter"), + verbose: require("./verboseFormatter"), +} diff --git a/src/formatters/jsonFormatter.js b/lib/formatters/jsonFormatter.js similarity index 73% rename from src/formatters/jsonFormatter.js rename to lib/formatters/jsonFormatter.js index 48aa15d3ee..e0d81dcf17 100644 --- a/src/formatters/jsonFormatter.js +++ b/lib/formatters/jsonFormatter.js @@ -1,7 +1,9 @@ -import _ from "lodash" +"use strict" + +const _ = require("lodash") // Omit any properties starting with `_`, which are fake-private -export default function (results) { +module.exports = function (results) { const cleanedResults = results.map(result => { return _.omitBy(result, (value, key) => key[0] === "_") }) diff --git a/src/formatters/needlessDisablesStringFormatter.js b/lib/formatters/needlessDisablesStringFormatter.js similarity index 81% rename from src/formatters/needlessDisablesStringFormatter.js rename to lib/formatters/needlessDisablesStringFormatter.js index 126cc5fb85..b6aad30ec9 100644 --- a/src/formatters/needlessDisablesStringFormatter.js +++ b/lib/formatters/needlessDisablesStringFormatter.js @@ -1,16 +1,20 @@ -import chalk from "chalk" -import path from "path" +"use strict" + +const chalk = require("chalk") +const path = require("path") function logFrom(fromValue) { if (fromValue.charAt(0) === "<") return fromValue return path.relative(process.cwd(), fromValue).split(path.sep).join("/") } -export default function (report) { +module.exports = function (report) { let output = "" report.forEach(sourceReport => { - if (!sourceReport.ranges || sourceReport.ranges.length === 0) { return } + if (!sourceReport.ranges || sourceReport.ranges.length === 0) { + return + } output += "\n" output += chalk.underline(logFrom(sourceReport.source)) + "\n" sourceReport.ranges.forEach(range => { diff --git a/src/formatters/stringFormatter.js b/lib/formatters/stringFormatter.js similarity index 60% rename from src/formatters/stringFormatter.js rename to lib/formatters/stringFormatter.js index eeea4d122d..5dce1af306 100644 --- a/src/formatters/stringFormatter.js +++ b/lib/formatters/stringFormatter.js @@ -1,10 +1,12 @@ -import { getBorderCharacters, table } from "table" -import _ from "lodash" -import chalk from "chalk" -import path from "path" -import stringWidth from "string-width" -import symbols from "log-symbols" -import utils from "postcss-reporter/lib/util" +"use strict" + +const table = require("table") +const _ = require("lodash") +const chalk = require("chalk") +const path = require("path") +const stringWidth = require("string-width") +const symbols = require("log-symbols") +const utils = require("postcss-reporter/lib/util") const MARGIN_WIDTHS = 9 @@ -18,7 +20,9 @@ function deprecationsFormatter(results) { const allDeprecationWarnings = _.flatMap(results, "deprecations") const uniqueDeprecationWarnings = _.uniqBy(allDeprecationWarnings, "text") - if (!uniqueDeprecationWarnings || !uniqueDeprecationWarnings.length) { return "" } + if (!uniqueDeprecationWarnings || !uniqueDeprecationWarnings.length) { + return "" + } return uniqueDeprecationWarnings.reduce((output, warning) => { output += chalk.yellow("Deprecation Warning: ") @@ -66,12 +70,8 @@ function getMessageWidth(columnWidths) { function formatter(messages, source) { if (!messages.length) return "" - const orderedMessages = _.sortBy( - messages, - (m) => m.line ? 2 : 1, // positionless first - (m) => m.line, - (m) => m.column - ) + const orderedMessages = _.sortBy(messages, m => m.line ? 2 : 1, // positionless first + m => m.line, m => m.column) // Create a list of column widths, needed to calculate // the size of the message column and if needed wrap it. @@ -92,50 +92,35 @@ function formatter(messages, source) { output += chalk.underline(logFrom(source)) + "\n" } - const cleanedMessages = orderedMessages.map( - (message) => { - const location = utils.getLocation(message) - const severity = message.severity - const row = [ - location.line || "", - location.column || "", - symbols[severity] ? chalk[levelColors[severity]](symbols[severity]) : severity, - message - .text - // Remove all control characters (newline, tab and etc) - .replace(/[\x01-\x1A]+/g, " ") // eslint-disable-line - .replace(/\.$/, "") - .replace(new RegExp(_.escapeRegExp("(" + message.rule + ")") + "$"), ""), - chalk.dim(message.rule || ""), - ] - - calculateWidths(row) - - return row - }) - - output += table( - cleanedMessages, - { - border: getBorderCharacters("void"), - columns: { - 0: { alignment: "right", width: columnWidths[0], paddingRight: 0 }, - 1: { alignment: "left", width: columnWidths[1] }, - 2: { alignment: "center", width: columnWidths[2] }, - 3: { alignment: "left", width: getMessageWidth(columnWidths), wrapWord: true }, - 4: { alignment: "left", width: columnWidths[4], paddingRight: 0 }, - }, - drawHorizontalLine: () => false, - } - ) - .split("\n") - .map((el) => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(p1 + ":" + p2))) - .join("\n") + const cleanedMessages = orderedMessages.map(message => { + const location = utils.getLocation(message) + const severity = message.severity + const row = [ location.line || "", location.column || "", symbols[severity] ? chalk[levelColors[severity]](symbols[severity]) : severity, message.text + // Remove all control characters (newline, tab and etc) + .replace(/[\x01-\x1A]+/g, " ") // eslint-disable-line + .replace(/\.$/, "").replace(new RegExp(_.escapeRegExp("(" + message.rule + ")") + "$"), ""), chalk.dim(message.rule || "") ] + + calculateWidths(row) + + return row + }) + + output += table.table(cleanedMessages, { + border: table.getBorderCharacters("void"), + columns: { + 0: { alignment: "right", width: columnWidths[0], paddingRight: 0 }, + 1: { alignment: "left", width: columnWidths[1] }, + 2: { alignment: "center", width: columnWidths[2] }, + 3: { alignment: "left", width: getMessageWidth(columnWidths), wrapWord: true }, + 4: { alignment: "left", width: columnWidths[4], paddingRight: 0 }, + }, + drawHorizontalLine: () => false, + }).split("\n").map(el => el.replace(/(\d+)\s+(\d+)/, (m, p1, p2) => chalk.dim(p1 + ":" + p2))).join("\n") return output } -export default function (results) { +module.exports = function (results) { let output = invalidOptionsFormatter(results) output += deprecationsFormatter(results) diff --git a/src/formatters/verboseFormatter.js b/lib/formatters/verboseFormatter.js similarity index 67% rename from src/formatters/verboseFormatter.js rename to lib/formatters/verboseFormatter.js index 177d4a2625..4e770d4334 100644 --- a/src/formatters/verboseFormatter.js +++ b/lib/formatters/verboseFormatter.js @@ -1,17 +1,19 @@ -import _ from "lodash" -import chalk from "chalk" -import stringFormatter from "./stringFormatter" +"use strict" -export default function (results) { +const _ = require("lodash") +const chalk = require("chalk") +const stringFormatter = require("./stringFormatter") + +module.exports = function (results) { let output = stringFormatter(results) - if (output === "") { output = "\n" } + if (output === "") { + output = "\n" + } - const sourceWord = (results.length > 1) ? "sources" : "source" + const sourceWord = results.length > 1 ? "sources" : "source" const ignoredCount = results.filter(result => result.ignored).length - const checkedDisplay = (ignoredCount) - ? `${results.length - ignoredCount} of ${results.length}` - : results.length + const checkedDisplay = ignoredCount ? `${results.length - ignoredCount} of ${results.length}` : results.length output += chalk.underline(`${checkedDisplay} ${sourceWord} checked\n`) results.forEach(result => { let formatting = "green" @@ -23,13 +25,15 @@ export default function (results) { formatting = "dim" } let sourceText = `${result.source}` - if (result.ignored) { sourceText += " (ignored)" } + if (result.ignored) { + sourceText += " (ignored)" + } output += _.get(chalk, formatting)(` ${sourceText}\n`) }) const warnings = _.flatten(results.map(r => r.warnings)) const warningsBySeverity = _.groupBy(warnings, "severity") - const problemWord = (warnings.length === 1) ? "problem" : "problems" + const problemWord = warnings.length === 1 ? "problem" : "problems" output += chalk.underline(`\n${warnings.length} ${problemWord} found\n`) diff --git a/lib/getConfigForFile.js b/lib/getConfigForFile.js new file mode 100644 index 0000000000..9c773d878f --- /dev/null +++ b/lib/getConfigForFile.js @@ -0,0 +1,43 @@ +/* @flow */ +"use strict" +const augmentConfigFull = require("./augmentConfig").augmentConfigFull +const configurationError = require("./utils/configurationError") +const path = require("path") + +module.exports = function (stylelint/*: stylelint$internalApi*/, searchPath/*:: ?: string*/)/*: Promise*/ { + searchPath = searchPath || process.cwd() + + const optionsConfig = stylelint._options.config + + if (optionsConfig !== undefined) { + const cached = stylelint._specifiedConfigCache.get(optionsConfig) + if (cached) return cached + + // stylelint._fullExplorer (cosmiconfig) is already configured to + // run augmentConfigFull; but since we're making up the result here, + // we need to manually run the transform + const augmentedResult = augmentConfigFull(stylelint, { + config: optionsConfig, + // Add the extra path part so that we can get the directory without being + // confused + filepath: path.join(process.cwd(), "argument-config"), + }) + stylelint._specifiedConfigCache.set(optionsConfig, augmentedResult) + return augmentedResult + } + + return stylelint._fullExplorer.load(searchPath, stylelint._options.configFile).then(config => { + // If no config was found, try looking from process.cwd + if (!config) return stylelint._fullExplorer.load(process.cwd()) + return config + }).then(config => { + if (!config) { + const ending = searchPath ? ` for ${searchPath}` : "" + throw configurationError(`No configuration provided${ending}`) + } + return config + }) +} diff --git a/lib/getPostcssResult.js b/lib/getPostcssResult.js new file mode 100644 index 0000000000..472aaddb1a --- /dev/null +++ b/lib/getPostcssResult.js @@ -0,0 +1,97 @@ +/* @flow */ +"use strict" +const fs = require("fs") +const lessSyntax = require("postcss-less") +const path = require("path") +const postcss = require("postcss") +const scssSyntax = require("postcss-scss") +const sugarssSyntax = require("sugarss") + +const postcssProcessor = postcss() + +module.exports = function (stylelint/*: stylelint$internalApi*/)/*: Promise*/ { + const options/*: { + code?: string, + codeFilename?: string, + filePath?: string, + codeProcessors?: Array, + syntax?: stylelint$syntaxes, + customSyntax?: string + }*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {} + + const cached = stylelint._postcssResultCache.get(options.filePath) + if (cached) return Promise.resolve(cached) + + let getCode + if (options.code !== undefined) { + getCode = Promise.resolve(options.code) + } else if (options.filePath) { + getCode = readFile(options.filePath) + } + + if (!getCode) { + throw new Error("code or filePath required") + } + + return getCode.then(code => { + const customSyntax = stylelint._options.customSyntax + let syntax = stylelint._options.syntax + + if (customSyntax) { + try { + syntax = require(customSyntax) + } catch (e) { + throw new Error(`Cannot resolve custom syntax module ${customSyntax}`) + } + } else { + const fileExtension = path.extname(options.filePath || "") + if (syntax === "scss" || !syntax && fileExtension === ".scss") { + syntax = scssSyntax + } else if (syntax === "less" || !syntax && fileExtension === ".less") { + syntax = lessSyntax + } else if (syntax === "sugarss" || !syntax && fileExtension === ".sss") { + syntax = sugarssSyntax + } else if (syntax) { + throw new Error("You must use a valid syntax option, either: scss, less or sugarss") + } + } + + const postcssOptions = {} + + postcssOptions.from = options.filePath + + /* + * PostCSS allows for syntaxes that only contain a parser, however, + * it then expects the syntax to be set as the `parser` option rather than `syntax. + */ + if (syntax && !syntax.stringify) { + postcssOptions.parser = syntax + } else { + postcssOptions.syntax = syntax + } + + const source = options.code ? options.codeFilename : options.filePath + let preProcessedCode = code + if (options.codeProcessors) { + options.codeProcessors.forEach(codeProcessor => { + preProcessedCode = codeProcessor(preProcessedCode, source) + }) + } + + return postcssProcessor.process(preProcessedCode, postcssOptions) + }).then(postcssResult => { + stylelint._postcssResultCache.set(options.filePath, postcssResult) + return postcssResult + }) +} + +function readFile(filePath/*: string*/)/*: Promise*/ { + return new Promise((resolve, reject) => { + fs.readFile(filePath, "utf8", (err, content) => { + if (err) { + return reject(err) + } + resolve(content) + }) + }) +} diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000000..0c33a8b957 --- /dev/null +++ b/lib/index.js @@ -0,0 +1,27 @@ +"use strict" + +const report = require("./utils/report") +const ruleMessages = require("./utils/ruleMessages") +const validateOptions = require("./utils/validateOptions") +const createPlugin = require("./createPlugin") +const createRuleTester = require("./testUtils/createRuleTester") +const createStylelint = require("./createStylelint") +const postcssPlugin = require("./postcssPlugin") +const rules = require("./rules") +const standalone = require("./standalone") + +const api = postcssPlugin + +api.utils = { + report, + ruleMessages, + validateOptions, +} + +api.lint = standalone +api.rules = rules +api.createPlugin = createPlugin +api.createRuleTester = createRuleTester +api.createLinter = createStylelint + +module.exports = api diff --git a/src/isPathIgnored.js b/lib/isPathIgnored.js similarity index 59% rename from src/isPathIgnored.js rename to lib/isPathIgnored.js index 351b226d29..e9be70ab93 100644 --- a/src/isPathIgnored.js +++ b/lib/isPathIgnored.js @@ -1,24 +1,24 @@ /* @flow */ -import ignore from "ignore" -import multimatch from "multimatch" -import path from "path" +"use strict" +const ignore = require("ignore") +const multimatch = require("multimatch") +const path = require("path") // To find out if a path is ignored, we need to load the config, // which may have an ignoreFiles property, // and will have incorporated any .stylelintignore file that was found // into its ignorePatterns property. We then check the path // against these. -export default function ( - stylelint: stylelint$internalApi, - filePathArg?: string, -): Promise { +module.exports = function (stylelint/*: stylelint$internalApi*/, filePathArg/*:: ?: string*/)/*: Promise*/ { const filePath = filePathArg // to please Flow - if (!filePath) { return Promise.resolve(false) } + if (!filePath) { + return Promise.resolve(false) + } - return stylelint.getConfigForFile(filePath).then(({ config }) => { - const absoluteFilePath = (path.isAbsolute(filePath)) - ? filePath - : path.resolve(process.cwd(), filePath) + return stylelint.getConfigForFile(filePath).then((result) => { + const config = result.config + + const absoluteFilePath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath) if (config.ignoreFiles && multimatch(absoluteFilePath, config.ignoreFiles).length) { return true diff --git a/src/lintSource.js b/lib/lintSource.js similarity index 64% rename from src/lintSource.js rename to lib/lintSource.js index 2edab73bbf..d0d0a8394f 100644 --- a/src/lintSource.js +++ b/lib/lintSource.js @@ -1,37 +1,34 @@ /* @flow */ -import _ from "lodash" -import assignDisabledRanges from "./assignDisabledRanges" -import { configurationError } from "./utils" -import ruleDefinitions from "./rules" +"use strict" +const _ = require("lodash") +const assignDisabledRanges = require("./assignDisabledRanges") +const configurationError = require("./utils/configurationError") +const ruleDefinitions = require("./rules") // Run stylelint on a PostCSS Result, either one that is provided // or one that we create -export default function ( - stylelint: stylelint$internalApi, - options: { - code?: string, - codeFilename?: string, - filePath?: string, - existingPostcssResult?: Object, - } = {}, -): Promise { +module.exports = function (stylelint/*: stylelint$internalApi*/)/*: Promise*/ { + const options/*: { + code?: string, + codeFilename?: string, + filePath?: string, + existingPostcssResult?: Object, + }*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {} + if (!options.filePath && options.code === undefined && !options.existingPostcssResult) { return Promise.reject("You must provide filePath, code, or existingPostcssResult") } const isCodeNotFile = options.code !== undefined - const inputFilePath = (isCodeNotFile) - ? options.codeFilename - : options.filePath + const inputFilePath = isCodeNotFile ? options.codeFilename : options.filePath - const getIsIgnored = stylelint.isPathIgnored(inputFilePath) - .catch((err) => { - if (isCodeNotFile && err.code === "ENOENT") return false - throw err - }) + const getIsIgnored = stylelint.isPathIgnored(inputFilePath).catch(err => { + if (isCodeNotFile && err.code === "ENOENT") return false + throw err + }) - return getIsIgnored.then((isIgnored) => { + return getIsIgnored.then(isIgnored => { if (isIgnored) { const postcssResult = options.existingPostcssResult || createEmptyPostcssResult(inputFilePath) postcssResult.stylelint = postcssResult.stylelint || {} @@ -42,17 +39,17 @@ export default function ( const configSearchPath = stylelint._options.configFile || inputFilePath - const getConfig = stylelint.getConfigForFile(configSearchPath) - .catch((err) => { - if (isCodeNotFile && err.code === "ENOENT") return stylelint.getConfigForFile(process.cwd()) - throw err - }) + const getConfig = stylelint.getConfigForFile(configSearchPath).catch(err => { + if (isCodeNotFile && err.code === "ENOENT") return stylelint.getConfigForFile(process.cwd()) + throw err + }) + + return getConfig.then((result) => { + const config = result.config + const existingPostcssResult = options.existingPostcssResult - return getConfig.then(({ config }) => { - const { existingPostcssResult } = options if (existingPostcssResult) { - return lintPostcssResult(stylelint, existingPostcssResult, config) - .then(() => existingPostcssResult) + return lintPostcssResult(stylelint, existingPostcssResult, config).then(() => existingPostcssResult) } return stylelint._getPostcssResult({ @@ -60,19 +57,14 @@ export default function ( codeFilename: options.codeFilename, filePath: inputFilePath, codeProcessors: config.codeProcessors, - }).then((postcssResult) => { - return lintPostcssResult(stylelint, postcssResult, config) - .then(() => postcssResult) + }).then(postcssResult => { + return lintPostcssResult(stylelint, postcssResult, config).then(() => postcssResult) }) }) }) } -function lintPostcssResult( - stylelint: stylelint$internalApi, - postcssResult: Object, - config: stylelint$config, -): Promise<> { +function lintPostcssResult(stylelint/*: stylelint$internalApi*/, postcssResult/*: Object*/, config/*: stylelint$config*/)/*: Promise<>*/ { postcssResult.stylelint = postcssResult.stylelint || {} postcssResult.stylelint.ruleSeverities = {} postcssResult.stylelint.customMessages = {} @@ -89,9 +81,9 @@ function lintPostcssResult( // rules down the line. const performRules = [] - const rules = (config.rules) ? Object.keys(config.rules) : [] + const rules = config.rules ? Object.keys(config.rules) : [] - rules.forEach((ruleName) => { + rules.forEach(ruleName => { const ruleFunction = ruleDefinitions[ruleName] || _.get(config, [ "pluginFunctions", ruleName ]) if (ruleFunction === undefined) { @@ -99,7 +91,9 @@ function lintPostcssResult( } const ruleSettings = _.get(config, [ "rules", ruleName ]) - if (ruleSettings === null || ruleSettings[0] === null) { return } + if (ruleSettings === null || ruleSettings[0] === null) { + return + } const primaryOption = ruleSettings[0] const secondaryOptions = ruleSettings[1] @@ -118,7 +112,7 @@ function lintPostcssResult( return Promise.all(performRules) } -function createEmptyPostcssResult(filePath?: string): Object { +function createEmptyPostcssResult(filePath/*:: ?: string*/)/*: Object*/ { return { root: { source: { diff --git a/src/needlessDisables.js b/lib/needlessDisables.js similarity index 65% rename from src/needlessDisables.js rename to lib/needlessDisables.js index 9c95c5cace..ccc02e8276 100644 --- a/src/needlessDisables.js +++ b/lib/needlessDisables.js @@ -1,22 +1,26 @@ +"use strict" + /* flow */ -import _ from "lodash" +const _ = require("lodash") -export default function ( - results: Array -): stylelint$needlessDisablesReport { +module.exports = function (results/*: Array*/)/*: stylelint$needlessDisablesReport*/ { const report = [] results.forEach(result => { // File with `CssSyntaxError` have not `_postcssResult` - if (!result._postcssResult) { return } + if (!result._postcssResult) { + return + } const unused = { source: result.source, ranges: [] } const rangeData = _.cloneDeep(result._postcssResult.stylelint.disabledRanges) - if (!rangeData) { return } + if (!rangeData) { + return + } result.warnings.forEach(warning => { - const { rule } = warning + const rule = warning.rule const ruleRanges = rangeData[rule] if (ruleRanges) { @@ -67,19 +71,16 @@ export default function ( return report } -function isWarningInRange( - warning: { - rule: string, - line: number, - }, - range: { - rules?: Array, - start: number, - end?: number, - }, -): boolean { - const { rule, line } = warning - return range.start <= line - && (range.end >= line || range.end === undefined) - && (!range.rules || range.rules.indexOf(rule) !== -1) +function isWarningInRange(warning/*: { + rule: string, + line: number, + }*/, range/*: { + rules?: Array, + start: number, + end?: number, + }*/)/*: boolean*/ { + const rule = warning.rule, + line = warning.line + + return range.start <= line && (range.end >= line || range.end === undefined) && (!range.rules || range.rules.indexOf(rule) !== -1) } diff --git a/src/normalizeRuleSettings.js b/lib/normalizeRuleSettings.js similarity index 55% rename from src/normalizeRuleSettings.js rename to lib/normalizeRuleSettings.js index 7d78a697cd..f969871ebe 100644 --- a/src/normalizeRuleSettings.js +++ b/lib/normalizeRuleSettings.js @@ -1,5 +1,6 @@ /* @flow */ -import _ from "lodash" +"use strict" +const _ = require("lodash") // Rule settings can take a number of forms, e.g. // a. "rule-name": null @@ -13,36 +14,44 @@ import _ from "lodash" // standard form: [primaryOption, secondaryOption] // Except in the cases with null, a & b, in which case // null is returned -export default function ( - rawSettings: stylelint$configRuleSettings, - ruleName: string, - primaryOptionArray?: boolean, -): ?Array { - if (rawSettings === null) { return null } - - if (!Array.isArray(rawSettings)) { return [rawSettings] } +module.exports = function (rawSettings/*: stylelint$configRuleSettings*/, ruleName/*: string*/, primaryOptionArray/*:: ?: boolean*/)/*: ?Array*/ { + if (rawSettings === null) { + return null + } + + if (!Array.isArray(rawSettings)) { + return [rawSettings] + } // Everything below is an array ... - if (rawSettings[0] === null) { return null } + if (rawSettings[0] === null) { + return null + } // This cursed rule needs a special case if (ruleName === "declaration-block-properties-order") { - if (rawSettings[0] === "alphabetical") { return rawSettings } - if (typeof rawSettings[0] === "string") { return [rawSettings] } + if (rawSettings[0] === "alphabetical") { + return rawSettings + } + if (typeof rawSettings[0] === "string") { + return [rawSettings] + } } - if (!primaryOptionArray) { return rawSettings } + if (!primaryOptionArray) { + return rawSettings + } // Everything below is a rule that CAN have an array for a primary option ... // (they might also have something else, e.g. rule-properties-order can // have the string "alphabetical") - if (rawSettings.length === 1 && Array.isArray(rawSettings[0])) { return rawSettings } + if (rawSettings.length === 1 && Array.isArray(rawSettings[0])) { + return rawSettings + } - if ( - rawSettings.length === 2 - && !_.isPlainObject(rawSettings[0]) - && _.isPlainObject(rawSettings[1]) - ) { return rawSettings } + if (rawSettings.length === 2 && !_.isPlainObject(rawSettings[0]) && _.isPlainObject(rawSettings[1])) { + return rawSettings + } return [rawSettings] } diff --git a/lib/postcssPlugin.js b/lib/postcssPlugin.js new file mode 100644 index 0000000000..ad7a641d85 --- /dev/null +++ b/lib/postcssPlugin.js @@ -0,0 +1,19 @@ +/* @flow */ +"use strict" +const _ = require("lodash") +const createStylelint = require("./createStylelint") +const postcss = require("postcss") + +module.exports = postcss.plugin("stylelint", function () { + const options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {} + + const tailoredOptions/*: Object*/ = options.rules ? { config: options } : options + + const stylelint = createStylelint(tailoredOptions) + return (root, result) => { + return stylelint._lintSource({ + filePath: options.from || _.get(root, "source.input.file"), + existingPostcssResult: result, + }) + } +}) diff --git a/lib/reference/keywordSets.js b/lib/reference/keywordSets.js new file mode 100644 index 0000000000..b68f6ac96b --- /dev/null +++ b/lib/reference/keywordSets.js @@ -0,0 +1,138 @@ +"use strict" + +const _ = require("lodash") + +const keywordSets = {} + +keywordSets.nonLengthUnits = new Set([ +// Relative length units + "%", +// Time length units + "s", "ms", +// Angle + "deg", "grad", "turn", "rad", +// Frequency + "Hz", "kHz", +// Resolution + "dpi", "dpcm", "dppx" ]) + +keywordSets.lengthUnits = new Set([ +// Relative length units + "em", "ex", "ch", "rem", +// Viewport-percentage lengths + "vh", "vw", "vmin", "vmax", "vm", +// Absolute length units + "px", "mm", "cm", "in", "pt", "pc", "q" ]) + +keywordSets.units = uniteSets(keywordSets.nonLengthUnits, keywordSets.lengthUnits) + +keywordSets.colorFunctionNames = new Set([ "rgb", "rgba", "hsl", "hsla", "hwb", "gray" ]) + +keywordSets.camelCaseFunctionNames = new Set([ "translateX", "translateY", "translateZ", "scaleX", "scaleY", "scaleZ", "rotateX", "rotateY", "rotateZ", "skewX", "skewY" ]) + +keywordSets.basicKeywords = new Set([ "initial", "inherit", "unset" ]) + +keywordSets.fontFamilyKeywords = uniteSets(keywordSets.basicKeywords, [ "serif", "sans-serif", "cursive", "fantasy", "monospace" ]) + +keywordSets.fontWeightRelativeKeywords = new Set([ "bolder", "lighter" ]) + +keywordSets.fontWeightAbsoluteKeywords = new Set(["bold"]) + +keywordSets.fontWeightNumericKeywords = new Set([ "100", "200", "300", "400", "500", "600", "700", "800", "900" ]) + +keywordSets.fontWeightKeywords = uniteSets(keywordSets.basicKeywords, keywordSets.fontWeightRelativeKeywords, keywordSets.fontWeightAbsoluteKeywords, keywordSets.fontWeightNumericKeywords) + +keywordSets.animationNameKeywords = uniteSets(keywordSets.basicKeywords, ["none"]) + +keywordSets.animationTimingFunctionKeywords = uniteSets(keywordSets.basicKeywords, [ "linear", "ease", "ease-in", "ease-in-out", "ease-out", "step-start", "step-end", "steps", "cubic-bezier" ]) + +keywordSets.animationIterationCountKeywords = new Set(["infinite"]) + +keywordSets.animationDirectionKeywords = uniteSets(keywordSets.basicKeywords, [ "normal", "reverse", "alternate", "alternate-reverse" ]) + +keywordSets.animationFillModeKeywords = new Set([ "none", "forwards", "backwards", "both" ]) + +keywordSets.animationPlayStateKeywords = uniteSets(keywordSets.basicKeywords, [ "running", "paused" ]) + +// cf. https://developer.mozilla.org/en-US/docs/Web/CSS/animation +keywordSets.animationShorthandKeywords = uniteSets(keywordSets.basicKeywords, keywordSets.animationNameKeywords, keywordSets.animationTimingFunctionKeywords, keywordSets.animationIterationCountKeywords, keywordSets.animationDirectionKeywords, keywordSets.animationFillModeKeywords, keywordSets.animationPlayStateKeywords) + +// These are the ones that can have single-colon notation +keywordSets.levelOneAndTwoPseudoElements = new Set([ "before", "after", "first-line", "first-letter" ]) + +// These are the ones that require double-colon notation +keywordSets.levelThreePseudoElements = new Set([ "before", "after", "first-line", "first-letter", "selection", "spelling-error", "grammar-error", "backdrop", "marker", "placeholder", "shadow", "slotted", "content" ]) + +keywordSets.pseudoElements = uniteSets(keywordSets.levelOneAndTwoPseudoElements, keywordSets.levelThreePseudoElements) + +keywordSets.aNPlusBNotationPseudoClasses = new Set([ "nth-child", "nth-column", "nth-last-child", "nth-last-column", "nth-last-of-type", "nth-of-type" ]) + +keywordSets.linguisticPseudoClasses = new Set([ "dir", "lang" ]) + +keywordSets.otherPseudoClasses = new Set([ "active", "any-link", "blank", "checked", "contains", "current", "default", "disabled", "drop", "empty", "enabled", "first-child", "first-of-type", "focus", "focus-within", "fullscreen", "future", "has", "host", "host-context", "hover", "indeterminate", "in-range", "invalid", "last-child", "last-of-type", "link", "matches", "not", "only-child", "only-of-type", "optional", "out-of-range", "past", "placeholder-shown", "read-only", "read-write", "required", "root", "scope", "target", "user-error", "user-invalid", "val", "valid", "visited" ]) + +keywordSets.pseudoClasses = uniteSets(keywordSets.aNPlusBNotationPseudoClasses, keywordSets.linguisticPseudoClasses, keywordSets.otherPseudoClasses) + +keywordSets.shorthandTimeProperties = new Set([ "transition", "animation" ]) + +keywordSets.longhandTimeProperties = new Set([ "transition-duration", "transition-delay", "animation-duration", "animation-delay" ]) + +keywordSets.timeProperties = uniteSets(keywordSets.shorthandTimeProperties, keywordSets.longhandTimeProperties) + +keywordSets.camelCaseKeywords = new Set([ "optimizeSpeed", "optimizeQuality", "optimizeLegibility", "geometricPrecision", "currentColor", "crispEdges", "visiblePainted", "visibleFill", "visibleStroke", "sRGB", "linearRGB" ]) + +// https://developer.mozilla.org/docs/Web/CSS/counter-increment +keywordSets.counterIncrementKeywords = uniteSets(keywordSets.basicKeywords, ["none"]) + +keywordSets.gridRowKeywords = uniteSets(keywordSets.basicKeywords, [ "auto", "span" ]) + +keywordSets.gridColumnKeywords = uniteSets(keywordSets.basicKeywords, [ "auto", "span" ]) + +keywordSets.gridAreaKeywords = uniteSets(keywordSets.basicKeywords, [ "auto", "span" ]) + +// https://developer.mozilla.org/ru/docs/Web/CSS/list-style-type +keywordSets.listStyleTypeKeywords = uniteSets(keywordSets.basicKeywords, [ "none", "disc", "circle", "square", "decimal", "cjk-decimal", "decimal-leading-zero", "lower-roman", "upper-roman", "lower-greek", "lower-alpha", "lower-latin", "upper-alpha", "upper-latin", "arabic-indic", "armenian", "bengali", "cambodian", "cjk-earthly-branch", "cjk-ideographic", "devanagari", "ethiopic-numeric", "georgian", "gujarati", "gurmukhi", "hebrew", "hiragana", "hiragana-iroha", "japanese-formal", "japanese-informal", "kannada", "katakana", "katakana-iroha", "khmer", "korean-hangul-formal", "korean-hanja-formal", "korean-hanja-informal", "lao", "lower-armenian", "malayalam", "mongolian", "myanmar", "oriya", "persian", "simp-chinese-formal", "simp-chinese-informal", "tamil", "telugu", "thai", "tibetan", "trad-chinese-formal", "trad-chinese-informal", "upper-armenian", "disclosure-open", "disclosure-closed", +// Non-standard extensions (without prefixe) + "ethiopic-halehame", "ethiopic-halehame-am", "ethiopic-halehame-ti-er", "ethiopic-halehame-ti-et", "hangul", "hangul-consonant", "urdu" ]) + +keywordSets.listStylePositionKeywords = uniteSets(keywordSets.basicKeywords, [ "inside", "outside" ]) + +keywordSets.listStyleImageKeywords = uniteSets(keywordSets.basicKeywords, ["none"]) + +keywordSets.listStyleShorthandKeywords = uniteSets(keywordSets.basicKeywords, keywordSets.listStyleTypeKeywords, keywordSets.listStylePositionKeywords, keywordSets.listStyleImageKeywords) + +keywordSets.fontStyleKeywords = uniteSets(keywordSets.basicKeywords, [ "normal", "italic", "oblique" ]) + +keywordSets.fontVariantKeywords = uniteSets(keywordSets.basicKeywords, [ "normal", "none", "historical-forms", "none", "common-ligatures", "no-common-ligatures", "discretionary-ligatures", "no-discretionary-ligatures", "historical-ligatures", "no-historical-ligatures", "contextual", "no-contextual", "small-caps", "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "unicase", "titling-caps", "lining-nums", "oldstyle-nums", "proportional-nums", "tabular-nums", "diagonal-fractions", "stacked-fractions", "ordinal", "slashed-zero", "jis78", "jis83", "jis90", "jis04", "simplified", "traditional", "full-width", "proportional-width", "ruby" ]) + +keywordSets.fontStretchKeywords = uniteSets(keywordSets.basicKeywords, [ "semi-condensed", "condensed", "extra-condensed", "ultra-condensed", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" ]) + +keywordSets.fontSizeKeywords = uniteSets(keywordSets.basicKeywords, [ "xx-small", "x-small", "small", "medium", "large", "x-large", "xx-large", "larger", "smaller" ]) + +keywordSets.lineHeightKeywords = uniteSets(keywordSets.basicKeywords, ["normal"]) + +keywordSets.fontShorthandKeywords = uniteSets(keywordSets.basicKeywords, keywordSets.fontStyleKeywords, keywordSets.fontVariantKeywords, keywordSets.fontWeightKeywords, keywordSets.fontStretchKeywords, keywordSets.fontSizeKeywords, keywordSets.lineHeightKeywords, keywordSets.fontFamilyKeywords) + +keywordSets.keyframeSelectorKeywords = new Set([ "from", "to" ]) + +// https://developer.mozilla.org/en/docs/Web/CSS/At-rule +keywordSets.atRules = new Set([ "apply", "annotation", "character-variant", "charset", "counter-style", "custom-media", "custom-selector", "document", "font-face", "font-feature-values", "import", "keyframes", "media", "namespace", "nest", "ornaments", "page", "styleset", "stylistic", "supports", "swash", "viewport" ]) + +// https://drafts.csswg.org/mediaqueries/#descdef-media-update +keywordSets.deprecatedMediaFeatureNames = new Set([ "device-aspect-ratio", "device-height", "device-width", "max-device-aspect-ratio", "max-device-height", "max-device-width", "min-device-aspect-ratio", "min-device-height", "min-device-width" ]) + +// https://drafts.csswg.org/mediaqueries/#descdef-media-update +keywordSets.mediaFeatureNames = uniteSets(keywordSets.deprecatedMediaFeatureNames, [ "any-hover", "any-pointer", "aspect-ratio", "color", "color-gamut", "color-index", "grid", "height", "hover", "max-aspect-ratio", "max-color", "max-color-index", "max-height", "max-monochrome", "max-resolution", "max-width", "min-aspect-ratio", "min-color", "min-color-index", "min-height", "min-monochrome", "min-resolution", "min-width", "monochrome", "orientation", "overflow-block", "overflow-inline", "pointer", "resolution", "scan", "scripting", "update", "width" ]) + +// https://www.w3.org/TR/CSS22/ui.html#system-colors +keywordSets.systemColors = new Set([ "activeborder", "activecaption", "appworkspace", "background", "buttonface", "buttonhighlight", "buttonshadow", "buttontext", "captiontext", "graytext", "highlight", "highlighttext", "inactiveborder", "inactivecaption", "inactivecaptiontext", "infobackground", "infotext", "menu", "menutext", "scrollbar", "threeddarkshadow", "threedface", "threedhighlight", "threedlightshadow", "threedshadow", "window", "windowframe", "windowtext" ]) + +function uniteSets() { + const sets = Array.from(arguments) + + return new Set(sets.reduce((result, set) => { + return result.concat(_.toArray(set)) + }, [])) +} + +module.exports = keywordSets diff --git a/lib/reference/namedColorData.js b/lib/reference/namedColorData.js new file mode 100644 index 0000000000..36ca003ec5 --- /dev/null +++ b/lib/reference/namedColorData.js @@ -0,0 +1,596 @@ +"use strict" + +module.exports = { + "aliceblue": { + "hex": [ "#f0f8ff", "#fff0f8ff" ], + "func": [ "rgb(240,248,255)", "rgba(240,248,255,1)", "rgba(240,248,255,100%)", "rgb(94%,97%,100%)", "rgba(94%,97%,100%,1)", "rgba(94%,97%,100%,100%)", "hsl(208,100%,97%)", "hsla(208,100%,97%,1)", "hsla(208,100%,97%,100%)", "hwb(208,94%,0%)", "hwb(208,94%,0%,1)", "hwb(208,94%,0%,100%)" ], + }, + "antiquewhite": { + "hex": [ "#faebd7", "#fffaebd7" ], + "func": [ "rgb(250,235,215)", "rgba(250,235,215,1)", "rgba(250,235,215,100%)", "rgb(98%,92%,84%)", "rgba(98%,92%,84%,1)", "rgba(98%,92%,84%,100%)", "hsl(34,78%,91%)", "hsla(34,78%,91%,1)", "hsla(34,78%,91%,100%)", "hwb(34,84%,2%)", "hwb(34,84%,2%,1)", "hwb(34,84%,2%,100%)" ], + }, + "aqua": { + "hex": [ "#00ffff", "#ff00ffff", "#0ff", "#f0ff" ], + "func": [ "rgb(0,255,255)", "rgba(0,255,255,1)", "rgba(0,255,255,100%)", "rgb(0%,100%,100%)", "rgba(0%,100%,100%,1)", "rgba(0%,100%,100%,100%)", "hsl(180,100%,50%)", "hsla(180,100%,50%,1)", "hsla(180,100%,50%,100%)", "hwb(180,0%,0%)", "hwb(180,0%,0%,1)", "hwb(180,0%,0%,100%)" ], + }, + "aquamarine": { + "hex": [ "#7fffd4", "#ff7fffd4" ], + "func": [ "rgb(127,255,212)", "rgba(127,255,212,1)", "rgba(127,255,212,100%)", "rgb(50%,100%,83%)", "rgba(50%,100%,83%,1)", "rgba(50%,100%,83%,100%)", "hsl(160,100%,75%)", "hsla(160,100%,75%,1)", "hsla(160,100%,75%,100%)", "hwb(160,50%,0%)", "hwb(160,50%,0%,1)", "hwb(160,50%,0%,100%)" ], + }, + "azure": { + "hex": [ "#f0ffff", "#fff0ffff" ], + "func": [ "rgb(240,255,255)", "rgba(240,255,255,1)", "rgba(240,255,255,100%)", "rgb(94%,100%,100%)", "rgba(94%,100%,100%,1)", "rgba(94%,100%,100%,100%)", "hsl(180,100%,97%)", "hsla(180,100%,97%,1)", "hsla(180,100%,97%,100%)", "hwb(180,94%,0%)", "hwb(180,94%,0%,1)", "hwb(180,94%,0%,100%)" ], + }, + "beige": { + "hex": [ "#f5f5dc", "#fff5f5dc" ], + "func": [ "rgb(245,245,220)", "rgba(245,245,220,1)", "rgba(245,245,220,100%)", "rgb(96%,96%,86%)", "rgba(96%,96%,86%,1)", "rgba(96%,96%,86%,100%)", "hsl(60,56%,91%)", "hsla(60,56%,91%,1)", "hsla(60,56%,91%,100%)", "hwb(60,86%,4%)", "hwb(60,86%,4%,1)", "hwb(60,86%,4%,100%)" ], + }, + "bisque": { + "hex": [ "#ffe4c4", "#ffffe4c4" ], + "func": [ "rgb(255,228,196)", "rgba(255,228,196,1)", "rgba(255,228,196,100%)", "rgb(100%,89%,77%)", "rgba(100%,89%,77%,1)", "rgba(100%,89%,77%,100%)", "hsl(33,100%,88%)", "hsla(33,100%,88%,1)", "hsla(33,100%,88%,100%)", "hwb(33,77%,0%)", "hwb(33,77%,0%,1)", "hwb(33,77%,0%,100%)" ], + }, + "black": { + "hex": [ "#000000", "#ff000000", "#000", "#f000" ], + "func": [ "rgb(0,0,0)", "rgba(0,0,0,1)", "rgba(0,0,0,100%)", "rgb(0%,0%,0%)", "rgba(0%,0%,0%,1)", "rgba(0%,0%,0%,100%)", "hsl(0,0%,0%)", "hsla(0,0%,0%,1)", "hsla(0,0%,0%,100%)", "hwb(0,0%,100%)", "hwb(0,0%,100%,1)", "hwb(0,0%,100%,100%)", "gray(0)", "gray(0,1)", "gray(0,100%)", "gray(0%)", "gray(0%,1)", "gray(0%,100%)" ], + }, + "blanchedalmond": { + "hex": [ "#ffebcd", "#ffffebcd" ], + "func": [ "rgb(255,235,205)", "rgba(255,235,205,1)", "rgba(255,235,205,100%)", "rgb(100%,92%,80%)", "rgba(100%,92%,80%,1)", "rgba(100%,92%,80%,100%)", "hsl(36,100%,90%)", "hsla(36,100%,90%,1)", "hsla(36,100%,90%,100%)", "hwb(36,80%,0%)", "hwb(36,80%,0%,1)", "hwb(36,80%,0%,100%)" ], + }, + "blue": { + "hex": [ "#0000ff", "#ff0000ff", "#00f", "#f00f" ], + "func": [ "rgb(0,0,255)", "rgba(0,0,255,1)", "rgba(0,0,255,100%)", "rgb(0%,0%,100%)", "rgba(0%,0%,100%,1)", "rgba(0%,0%,100%,100%)", "hsl(240,100%,50%)", "hsla(240,100%,50%,1)", "hsla(240,100%,50%,100%)", "hwb(240,0%,0%)", "hwb(240,0%,0%,1)", "hwb(240,0%,0%,100%)" ], + }, + "blueviolet": { + "hex": [ "#8a2be2", "#ff8a2be2" ], + "func": [ "rgb(138,43,226)", "rgba(138,43,226,1)", "rgba(138,43,226,100%)", "rgb(54%,17%,89%)", "rgba(54%,17%,89%,1)", "rgba(54%,17%,89%,100%)", "hsl(271,76%,53%)", "hsla(271,76%,53%,1)", "hsla(271,76%,53%,100%)", "hwb(271,17%,11%)", "hwb(271,17%,11%,1)", "hwb(271,17%,11%,100%)" ], + }, + "brown": { + "hex": [ "#a52a2a", "#ffa52a2a" ], + "func": [ "rgb(165,42,42)", "rgba(165,42,42,1)", "rgba(165,42,42,100%)", "rgb(65%,16%,16%)", "rgba(65%,16%,16%,1)", "rgba(65%,16%,16%,100%)", "hsl(0,59%,41%)", "hsla(0,59%,41%,1)", "hsla(0,59%,41%,100%)", "hwb(0,16%,35%)", "hwb(0,16%,35%,1)", "hwb(0,16%,35%,100%)" ], + }, + "burlywood": { + "hex": [ "#deb887", "#ffdeb887" ], + "func": [ "rgb(222,184,135)", "rgba(222,184,135,1)", "rgba(222,184,135,100%)", "rgb(87%,72%,53%)", "rgba(87%,72%,53%,1)", "rgba(87%,72%,53%,100%)", "hsl(34,57%,70%)", "hsla(34,57%,70%,1)", "hsla(34,57%,70%,100%)", "hwb(34,53%,13%)", "hwb(34,53%,13%,1)", "hwb(34,53%,13%,100%)" ], + }, + "cadetblue": { + "hex": [ "#5f9ea0", "#ff5f9ea0" ], + "func": [ "rgb(95,158,160)", "rgba(95,158,160,1)", "rgba(95,158,160,100%)", "rgb(37%,62%,63%)", "rgba(37%,62%,63%,1)", "rgba(37%,62%,63%,100%)", "hsl(182,25%,50%)", "hsla(182,25%,50%,1)", "hsla(182,25%,50%,100%)", "hwb(182,37%,37%)", "hwb(182,37%,37%,1)", "hwb(182,37%,37%,100%)" ], + }, + "chartreuse": { + "hex": [ "#7fff00", "#ff7fff00" ], + "func": [ "rgb(127,255,0)", "rgba(127,255,0,1)", "rgba(127,255,0,100%)", "rgb(50%,100%,0%)", "rgba(50%,100%,0%,1)", "rgba(50%,100%,0%,100%)", "hsl(90,100%,50%)", "hsla(90,100%,50%,1)", "hsla(90,100%,50%,100%)", "hwb(90,0%,0%)", "hwb(90,0%,0%,1)", "hwb(90,0%,0%,100%)" ], + }, + "chocolate": { + "hex": [ "#d2691e", "#ffd2691e" ], + "func": [ "rgb(210,105,30)", "rgba(210,105,30,1)", "rgba(210,105,30,100%)", "rgb(82%,41%,12%)", "rgba(82%,41%,12%,1)", "rgba(82%,41%,12%,100%)", "hsl(25,75%,47%)", "hsla(25,75%,47%,1)", "hsla(25,75%,47%,100%)", "hwb(25,12%,18%)", "hwb(25,12%,18%,1)", "hwb(25,12%,18%,100%)" ], + }, + "coral": { + "hex": [ "#ff7f50", "#ffff7f50" ], + "func": [ "rgb(255,127,80)", "rgba(255,127,80,1)", "rgba(255,127,80,100%)", "rgb(100%,50%,31%)", "rgba(100%,50%,31%,1)", "rgba(100%,50%,31%,100%)", "hsl(16,100%,66%)", "hsla(16,100%,66%,1)", "hsla(16,100%,66%,100%)", "hwb(16,31%,0%)", "hwb(16,31%,0%,1)", "hwb(16,31%,0%,100%)" ], + }, + "cornflowerblue": { + "hex": [ "#6495ed", "#ff6495ed" ], + "func": [ "rgb(100,149,237)", "rgba(100,149,237,1)", "rgba(100,149,237,100%)", "rgb(39%,58%,93%)", "rgba(39%,58%,93%,1)", "rgba(39%,58%,93%,100%)", "hsl(219,79%,66%)", "hsla(219,79%,66%,1)", "hsla(219,79%,66%,100%)", "hwb(219,39%,7%)", "hwb(219,39%,7%,1)", "hwb(219,39%,7%,100%)" ], + }, + "cornsilk": { + "hex": [ "#fff8dc", "#fffff8dc" ], + "func": [ "rgb(255,248,220)", "rgba(255,248,220,1)", "rgba(255,248,220,100%)", "rgb(100%,97%,86%)", "rgba(100%,97%,86%,1)", "rgba(100%,97%,86%,100%)", "hsl(48,100%,93%)", "hsla(48,100%,93%,1)", "hsla(48,100%,93%,100%)", "hwb(48,86%,0%)", "hwb(48,86%,0%,1)", "hwb(48,86%,0%,100%)" ], + }, + "crimson": { + "hex": [ "#dc143c", "#ffdc143c" ], + "func": [ "rgb(220,20,60)", "rgba(220,20,60,1)", "rgba(220,20,60,100%)", "rgb(86%,8%,24%)", "rgba(86%,8%,24%,1)", "rgba(86%,8%,24%,100%)", "hsl(348,83%,47%)", "hsla(348,83%,47%,1)", "hsla(348,83%,47%,100%)", "hwb(348,8%,14%)", "hwb(348,8%,14%,1)", "hwb(348,8%,14%,100%)" ], + }, + "cyan": { + "hex": [ "#00ffff", "#ff00ffff", "#0ff", "#f0ff" ], + "func": [ "rgb(0,255,255)", "rgba(0,255,255,1)", "rgba(0,255,255,100%)", "rgb(0%,100%,100%)", "rgba(0%,100%,100%,1)", "rgba(0%,100%,100%,100%)", "hsl(180,100%,50%)", "hsla(180,100%,50%,1)", "hsla(180,100%,50%,100%)", "hwb(180,0%,0%)", "hwb(180,0%,0%,1)", "hwb(180,0%,0%,100%)" ], + }, + "darkblue": { + "hex": [ "#00008b", "#ff00008b" ], + "func": [ "rgb(0,0,139)", "rgba(0,0,139,1)", "rgba(0,0,139,100%)", "rgb(0%,0%,55%)", "rgba(0%,0%,55%,1)", "rgba(0%,0%,55%,100%)", "hsl(240,100%,27%)", "hsla(240,100%,27%,1)", "hsla(240,100%,27%,100%)", "hwb(240,0%,45%)", "hwb(240,0%,45%,1)", "hwb(240,0%,45%,100%)" ], + }, + "darkcyan": { + "hex": [ "#008b8b", "#ff008b8b" ], + "func": [ "rgb(0,139,139)", "rgba(0,139,139,1)", "rgba(0,139,139,100%)", "rgb(0%,55%,55%)", "rgba(0%,55%,55%,1)", "rgba(0%,55%,55%,100%)", "hsl(180,100%,27%)", "hsla(180,100%,27%,1)", "hsla(180,100%,27%,100%)", "hwb(180,0%,45%)", "hwb(180,0%,45%,1)", "hwb(180,0%,45%,100%)" ], + }, + "darkgoldenrod": { + "hex": [ "#b8860b", "#ffb8860b" ], + "func": [ "rgb(184,134,11)", "rgba(184,134,11,1)", "rgba(184,134,11,100%)", "rgb(72%,53%,4%)", "rgba(72%,53%,4%,1)", "rgba(72%,53%,4%,100%)", "hsl(43,89%,38%)", "hsla(43,89%,38%,1)", "hsla(43,89%,38%,100%)", "hwb(43,4%,28%)", "hwb(43,4%,28%,1)", "hwb(43,4%,28%,100%)" ], + }, + "darkgray": { + "hex": [ "#a9a9a9", "#ffa9a9a9" ], + "func": [ "rgb(169,169,169)", "rgba(169,169,169,1)", "rgba(169,169,169,100%)", "rgb(66%,66%,66%)", "rgba(66%,66%,66%,1)", "rgba(66%,66%,66%,100%)", "hsl(0,0%,66%)", "hsla(0,0%,66%,1)", "hsla(0,0%,66%,100%)", "hwb(0,66%,34%)", "hwb(0,66%,34%,1)", "hwb(0,66%,34%,100%)" ], + }, + "darkgreen": { + "hex": [ "#006400", "#ff006400" ], + "func": [ "rgb(0,100,0)", "rgba(0,100,0,1)", "rgba(0,100,0,100%)", "rgb(0%,39%,0%)", "rgba(0%,39%,0%,1)", "rgba(0%,39%,0%,100%)", "hsl(120,100%,20%)", "hsla(120,100%,20%,1)", "hsla(120,100%,20%,100%)", "hwb(120,0%,61%)", "hwb(120,0%,61%,1)", "hwb(120,0%,61%,100%)" ], + }, + "darkgrey": { + "hex": [ "#a9a9a9", "#ffa9a9a9" ], + "func": [ "rgb(169,169,169)", "rgba(169,169,169,1)", "rgba(169,169,169,100%)", "rgb(66%,66%,66%)", "rgba(66%,66%,66%,1)", "rgba(66%,66%,66%,100%)", "hsl(0,0%,66%)", "hsla(0,0%,66%,1)", "hsla(0,0%,66%,100%)", "hwb(0,66%,34%)", "hwb(0,66%,34%,1)", "hwb(0,66%,34%,100%)", "gray(169)", "gray(169,1)", "gray(169,100%)", "gray(169%)", "gray(169%,1)", "gray(169%,100%)" ], + }, + "darkkhaki": { + "hex": [ "#bdb76b", "#ffbdb76b" ], + "func": [ "rgb(189,183,107)", "rgba(189,183,107,1)", "rgba(189,183,107,100%)", "rgb(74%,72%,42%)", "rgba(74%,72%,42%,1)", "rgba(74%,72%,42%,100%)", "hsl(56,38%,58%)", "hsla(56,38%,58%,1)", "hsla(56,38%,58%,100%)", "hwb(56,42%,26%)", "hwb(56,42%,26%,1)", "hwb(56,42%,26%,100%)" ], + }, + "darkmagenta": { + "hex": [ "#8b008b", "#ff8b008b" ], + "func": [ "rgb(139,0,139)", "rgba(139,0,139,1)", "rgba(139,0,139,100%)", "rgb(55%,0%,55%)", "rgba(55%,0%,55%,1)", "rgba(55%,0%,55%,100%)", "hsl(300,100%,27%)", "hsla(300,100%,27%,1)", "hsla(300,100%,27%,100%)", "hwb(300,0%,45%)", "hwb(300,0%,45%,1)", "hwb(300,0%,45%,100%)" ], + }, + "darkolivegreen": { + "hex": [ "#556b2f", "#ff556b2f" ], + "func": [ "rgb(85,107,47)", "rgba(85,107,47,1)", "rgba(85,107,47,100%)", "rgb(33%,42%,18%)", "rgba(33%,42%,18%,1)", "rgba(33%,42%,18%,100%)", "hsl(82,39%,30%)", "hsla(82,39%,30%,1)", "hsla(82,39%,30%,100%)", "hwb(82,18%,58%)", "hwb(82,18%,58%,1)", "hwb(82,18%,58%,100%)" ], + }, + "darkorange": { + "hex": [ "#ff8c00", "#ffff8c00" ], + "func": [ "rgb(255,140,0)", "rgba(255,140,0,1)", "rgba(255,140,0,100%)", "rgb(100%,55%,0%)", "rgba(100%,55%,0%,1)", "rgba(100%,55%,0%,100%)", "hsl(33,100%,50%)", "hsla(33,100%,50%,1)", "hsla(33,100%,50%,100%)", "hwb(33,0%,0%)", "hwb(33,0%,0%,1)", "hwb(33,0%,0%,100%)" ], + }, + "darkorchid": { + "hex": [ "#9932cc", "#ff9932cc" ], + "func": [ "rgb(153,50,204)", "rgba(153,50,204,1)", "rgba(153,50,204,100%)", "rgb(60%,20%,80%)", "rgba(60%,20%,80%,1)", "rgba(60%,20%,80%,100%)", "hsl(280,61%,50%)", "hsla(280,61%,50%,1)", "hsla(280,61%,50%,100%)", "hwb(280,20%,20%)", "hwb(280,20%,20%,1)", "hwb(280,20%,20%,100%)" ], + }, + "darkred": { + "hex": [ "#8b0000", "#ff8b0000" ], + "func": [ "rgb(139,0,0)", "rgba(139,0,0,1)", "rgba(139,0,0,100%)", "rgb(55%,0%,0%)", "rgba(55%,0%,0%,1)", "rgba(55%,0%,0%,100%)", "hsl(0,100%,27%)", "hsla(0,100%,27%,1)", "hsla(0,100%,27%,100%)", "hwb(0,0%,45%)", "hwb(0,0%,45%,1)", "hwb(0,0%,45%,100%)" ], + }, + "darksalmon": { + "hex": [ "#e9967a", "#ffe9967a" ], + "func": [ "rgb(233,150,122)", "rgba(233,150,122,1)", "rgba(233,150,122,100%)", "rgb(91%,59%,48%)", "rgba(91%,59%,48%,1)", "rgba(91%,59%,48%,100%)", "hsl(15,72%,70%)", "hsla(15,72%,70%,1)", "hsla(15,72%,70%,100%)", "hwb(15,48%,9%)", "hwb(15,48%,9%,1)", "hwb(15,48%,9%,100%)" ], + }, + "darkseagreen": { + "hex": [ "#8fbc8f", "#ff8fbc8f" ], + "func": [ "rgb(143,188,143)", "rgba(143,188,143,1)", "rgba(143,188,143,100%)", "rgb(56%,74%,56%)", "rgba(56%,74%,56%,1)", "rgba(56%,74%,56%,100%)", "hsl(120,25%,65%)", "hsla(120,25%,65%,1)", "hsla(120,25%,65%,100%)", "hwb(120,56%,26%)", "hwb(120,56%,26%,1)", "hwb(120,56%,26%,100%)" ], + }, + "darkslateblue": { + "hex": [ "#483d8b", "#ff483d8b" ], + "func": [ "rgb(72,61,139)", "rgba(72,61,139,1)", "rgba(72,61,139,100%)", "rgb(28%,24%,55%)", "rgba(28%,24%,55%,1)", "rgba(28%,24%,55%,100%)", "hsl(248,39%,39%)", "hsla(248,39%,39%,1)", "hsla(248,39%,39%,100%)", "hwb(248,24%,45%)", "hwb(248,24%,45%,1)", "hwb(248,24%,45%,100%)" ], + }, + "darkslategray": { + "hex": [ "#2f4f4f", "#ff2f4f4f" ], + "func": [ "rgb(47,79,79)", "rgba(47,79,79,1)", "rgba(47,79,79,100%)", "rgb(18%,31%,31%)", "rgba(18%,31%,31%,1)", "rgba(18%,31%,31%,100%)", "hsl(180,25%,25%)", "hsla(180,25%,25%,1)", "hsla(180,25%,25%,100%)", "hwb(180,18%,69%)", "hwb(180,18%,69%,1)", "hwb(180,18%,69%,100%)" ], + }, + "darkslategrey": { + "hex": [ "#2f4f4f", "#ff2f4f4f" ], + "func": [ "rgb(47,79,79)", "rgba(47,79,79,1)", "rgba(47,79,79,100%)", "rgb(18%,31%,31%)", "rgba(18%,31%,31%,1)", "rgba(18%,31%,31%,100%)", "hsl(180,25%,25%)", "hsla(180,25%,25%,1)", "hsla(180,25%,25%,100%)", "hwb(180,18%,69%)", "hwb(180,18%,69%,1)", "hwb(180,18%,69%,100%)" ], + }, + "darkturquoise": { + "hex": [ "#00ced1", "#ff00ced1" ], + "func": [ "rgb(0,206,209)", "rgba(0,206,209,1)", "rgba(0,206,209,100%)", "rgb(0%,81%,82%)", "rgba(0%,81%,82%,1)", "rgba(0%,81%,82%,100%)", "hsl(181,100%,41%)", "hsla(181,100%,41%,1)", "hsla(181,100%,41%,100%)", "hwb(181,0%,18%)", "hwb(181,0%,18%,1)", "hwb(181,0%,18%,100%)" ], + }, + "darkviolet": { + "hex": [ "#9400d3", "#ff9400d3" ], + "func": [ "rgb(148,0,211)", "rgba(148,0,211,1)", "rgba(148,0,211,100%)", "rgb(58%,0%,83%)", "rgba(58%,0%,83%,1)", "rgba(58%,0%,83%,100%)", "hsl(282,100%,41%)", "hsla(282,100%,41%,1)", "hsla(282,100%,41%,100%)", "hwb(282,0%,17%)", "hwb(282,0%,17%,1)", "hwb(282,0%,17%,100%)" ], + }, + "deeppink": { + "hex": [ "#ff1493", "#ffff1493" ], + "func": [ "rgb(255,20,147)", "rgba(255,20,147,1)", "rgba(255,20,147,100%)", "rgb(100%,8%,58%)", "rgba(100%,8%,58%,1)", "rgba(100%,8%,58%,100%)", "hsl(328,100%,54%)", "hsla(328,100%,54%,1)", "hsla(328,100%,54%,100%)", "hwb(328,8%,0%)", "hwb(328,8%,0%,1)", "hwb(328,8%,0%,100%)" ], + }, + "deepskyblue": { + "hex": [ "#00bfff", "#ff00bfff" ], + "func": [ "rgb(0,191,255)", "rgba(0,191,255,1)", "rgba(0,191,255,100%)", "rgb(0%,75%,100%)", "rgba(0%,75%,100%,1)", "rgba(0%,75%,100%,100%)", "hsl(195,100%,50%)", "hsla(195,100%,50%,1)", "hsla(195,100%,50%,100%)", "hwb(195,0%,0%)", "hwb(195,0%,0%,1)", "hwb(195,0%,0%,100%)" ], + }, + "dimgray": { + "hex": [ "#696969", "#ff696969" ], + "func": [ "rgb(105,105,105)", "rgba(105,105,105,1)", "rgba(105,105,105,100%)", "rgb(41%,41%,41%)", "rgba(41%,41%,41%,1)", "rgba(41%,41%,41%,100%)", "hsl(0,0%,41%)", "hsla(0,0%,41%,1)", "hsla(0,0%,41%,100%)", "hwb(0,41%,59%)", "hwb(0,41%,59%,1)", "hwb(0,41%,59%,100%)" ], + }, + "dimgrey": { + "hex": [ "#696969", "#ff696969" ], + "func": [ "rgb(105,105,105)", "rgba(105,105,105,1)", "rgba(105,105,105,100%)", "rgb(41%,41%,41%)", "rgba(41%,41%,41%,1)", "rgba(41%,41%,41%,100%)", "hsl(0,0%,41%)", "hsla(0,0%,41%,1)", "hsla(0,0%,41%,100%)", "hwb(0,41%,59%)", "hwb(0,41%,59%,1)", "hwb(0,41%,59%,100%)", "gray(105)", "gray(105,1)", "gray(105,100%)", "gray(105%)", "gray(105%,1)", "gray(105%,100%)" ], + }, + "dodgerblue": { + "hex": [ "#1e90ff", "#ff1e90ff" ], + "func": [ "rgb(30,144,255)", "rgba(30,144,255,1)", "rgba(30,144,255,100%)", "rgb(12%,56%,100%)", "rgba(12%,56%,100%,1)", "rgba(12%,56%,100%,100%)", "hsl(210,100%,56%)", "hsla(210,100%,56%,1)", "hsla(210,100%,56%,100%)", "hwb(210,12%,0%)", "hwb(210,12%,0%,1)", "hwb(210,12%,0%,100%)" ], + }, + "firebrick": { + "hex": [ "#b22222", "#ffb22222" ], + "func": [ "rgb(178,34,34)", "rgba(178,34,34,1)", "rgba(178,34,34,100%)", "rgb(70%,13%,13%)", "rgba(70%,13%,13%,1)", "rgba(70%,13%,13%,100%)", "hsl(0,68%,42%)", "hsla(0,68%,42%,1)", "hsla(0,68%,42%,100%)", "hwb(0,13%,30%)", "hwb(0,13%,30%,1)", "hwb(0,13%,30%,100%)" ], + }, + "floralwhite": { + "hex": [ "#fffaf0", "#fffffaf0" ], + "func": [ "rgb(255,250,240)", "rgba(255,250,240,1)", "rgba(255,250,240,100%)", "rgb(100%,98%,94%)", "rgba(100%,98%,94%,1)", "rgba(100%,98%,94%,100%)", "hsl(40,100%,97%)", "hsla(40,100%,97%,1)", "hsla(40,100%,97%,100%)", "hwb(40,94%,0%)", "hwb(40,94%,0%,1)", "hwb(40,94%,0%,100%)" ], + }, + "forestgreen": { + "hex": [ "#228b22", "#ff228b22" ], + "func": [ "rgb(34,139,34)", "rgba(34,139,34,1)", "rgba(34,139,34,100%)", "rgb(13%,55%,13%)", "rgba(13%,55%,13%,1)", "rgba(13%,55%,13%,100%)", "hsl(120,61%,34%)", "hsla(120,61%,34%,1)", "hsla(120,61%,34%,100%)", "hwb(120,13%,45%)", "hwb(120,13%,45%,1)", "hwb(120,13%,45%,100%)" ], + }, + "fuchsia": { + "hex": [ "#ff00ff", "#ffff00ff", "#f0f", "#ff0f" ], + "func": [ "rgb(255,0,255)", "rgba(255,0,255,1)", "rgba(255,0,255,100%)", "rgb(100%,0%,100%)", "rgba(100%,0%,100%,1)", "rgba(100%,0%,100%,100%)", "hsl(300,100%,50%)", "hsla(300,100%,50%,1)", "hsla(300,100%,50%,100%)", "hwb(300,0%,0%)", "hwb(300,0%,0%,1)", "hwb(300,0%,0%,100%)" ], + }, + "gainsboro": { + "hex": [ "#dcdcdc", "#ffdcdcdc" ], + "func": [ "rgb(220,220,220)", "rgba(220,220,220,1)", "rgba(220,220,220,100%)", "rgb(86%,86%,86%)", "rgba(86%,86%,86%,1)", "rgba(86%,86%,86%,100%)", "hsl(0,0%,86%)", "hsla(0,0%,86%,1)", "hsla(0,0%,86%,100%)", "hwb(0,86%,14%)", "hwb(0,86%,14%,1)", "hwb(0,86%,14%,100%)" ], + }, + "ghostwhite": { + "hex": [ "#f8f8ff", "#fff8f8ff" ], + "func": [ "rgb(248,248,255)", "rgba(248,248,255,1)", "rgba(248,248,255,100%)", "rgb(97%,97%,100%)", "rgba(97%,97%,100%,1)", "rgba(97%,97%,100%,100%)", "hsl(240,100%,99%)", "hsla(240,100%,99%,1)", "hsla(240,100%,99%,100%)", "hwb(240,97%,0%)", "hwb(240,97%,0%,1)", "hwb(240,97%,0%,100%)" ], + }, + "gold": { + "hex": [ "#ffd700", "#ffffd700" ], + "func": [ "rgb(255,215,0)", "rgba(255,215,0,1)", "rgba(255,215,0,100%)", "rgb(100%,84%,0%)", "rgba(100%,84%,0%,1)", "rgba(100%,84%,0%,100%)", "hsl(51,100%,50%)", "hsla(51,100%,50%,1)", "hsla(51,100%,50%,100%)", "hwb(51,0%,0%)", "hwb(51,0%,0%,1)", "hwb(51,0%,0%,100%)" ], + }, + "goldenrod": { + "hex": [ "#daa520", "#ffdaa520" ], + "func": [ "rgb(218,165,32)", "rgba(218,165,32,1)", "rgba(218,165,32,100%)", "rgb(85%,65%,13%)", "rgba(85%,65%,13%,1)", "rgba(85%,65%,13%,100%)", "hsl(43,74%,49%)", "hsla(43,74%,49%,1)", "hsla(43,74%,49%,100%)", "hwb(43,13%,15%)", "hwb(43,13%,15%,1)", "hwb(43,13%,15%,100%)" ], + }, + "gray": { + "hex": [ "#808080", "#ff808080" ], + "func": [ "rgb(128,128,128)", "rgba(128,128,128,1)", "rgba(128,128,128,100%)", "rgb(50%,50%,50%)", "rgba(50%,50%,50%,1)", "rgba(50%,50%,50%,100%)", "hsl(0,0%,50%)", "hsla(0,0%,50%,1)", "hsla(0,0%,50%,100%)", "hwb(0,50%,50%)", "hwb(0,50%,50%,1)", "hwb(0,50%,50%,100%)" ], + }, + "green": { + "hex": [ "#008000", "#ff008000" ], + "func": [ "rgb(0,128,0)", "rgba(0,128,0,1)", "rgba(0,128,0,100%)", "rgb(0%,50%,0%)", "rgba(0%,50%,0%,1)", "rgba(0%,50%,0%,100%)", "hsl(120,100%,25%)", "hsla(120,100%,25%,1)", "hsla(120,100%,25%,100%)", "hwb(120,0%,50%)", "hwb(120,0%,50%,1)", "hwb(120,0%,50%,100%)" ], + }, + "greenyellow": { + "hex": [ "#adff2f", "#ffadff2f" ], + "func": [ "rgb(173,255,47)", "rgba(173,255,47,1)", "rgba(173,255,47,100%)", "rgb(68%,100%,18%)", "rgba(68%,100%,18%,1)", "rgba(68%,100%,18%,100%)", "hsl(84,100%,59%)", "hsla(84,100%,59%,1)", "hsla(84,100%,59%,100%)", "hwb(84,18%,0%)", "hwb(84,18%,0%,1)", "hwb(84,18%,0%,100%)" ], + }, + "grey": { + "hex": [ "#808080", "#ff808080" ], + "func": [ "rgb(128,128,128)", "rgba(128,128,128,1)", "rgba(128,128,128,100%)", "rgb(50%,50%,50%)", "rgba(50%,50%,50%,1)", "rgba(50%,50%,50%,100%)", "hsl(0,0%,50%)", "hsla(0,0%,50%,1)", "hsla(0,0%,50%,100%)", "hwb(0,50%,50%)", "hwb(0,50%,50%,1)", "hwb(0,50%,50%,100%)", "gray(128)", "gray(128,1)", "gray(128,100%)", "gray(128%)", "gray(128%,1)", "gray(128%,100%)" ], + }, + "honeydew": { + "hex": [ "#f0fff0", "#fff0fff0" ], + "func": [ "rgb(240,255,240)", "rgba(240,255,240,1)", "rgba(240,255,240,100%)", "rgb(94%,100%,94%)", "rgba(94%,100%,94%,1)", "rgba(94%,100%,94%,100%)", "hsl(120,100%,97%)", "hsla(120,100%,97%,1)", "hsla(120,100%,97%,100%)", "hwb(120,94%,0%)", "hwb(120,94%,0%,1)", "hwb(120,94%,0%,100%)" ], + }, + "hotpink": { + "hex": [ "#ff69b4", "#ffff69b4" ], + "func": [ "rgb(255,105,180)", "rgba(255,105,180,1)", "rgba(255,105,180,100%)", "rgb(100%,41%,71%)", "rgba(100%,41%,71%,1)", "rgba(100%,41%,71%,100%)", "hsl(330,100%,71%)", "hsla(330,100%,71%,1)", "hsla(330,100%,71%,100%)", "hwb(330,41%,0%)", "hwb(330,41%,0%,1)", "hwb(330,41%,0%,100%)" ], + }, + "indianred": { + "hex": [ "#cd5c5c", "#ffcd5c5c" ], + "func": [ "rgb(205,92,92)", "rgba(205,92,92,1)", "rgba(205,92,92,100%)", "rgb(80%,36%,36%)", "rgba(80%,36%,36%,1)", "rgba(80%,36%,36%,100%)", "hsl(0,53%,58%)", "hsla(0,53%,58%,1)", "hsla(0,53%,58%,100%)", "hwb(0,36%,20%)", "hwb(0,36%,20%,1)", "hwb(0,36%,20%,100%)" ], + }, + "indigo": { + "hex": [ "#4b0082", "#ff4b0082" ], + "func": [ "rgb(75,0,130)", "rgba(75,0,130,1)", "rgba(75,0,130,100%)", "rgb(29%,0%,51%)", "rgba(29%,0%,51%,1)", "rgba(29%,0%,51%,100%)", "hsl(275,100%,25%)", "hsla(275,100%,25%,1)", "hsla(275,100%,25%,100%)", "hwb(275,0%,49%)", "hwb(275,0%,49%,1)", "hwb(275,0%,49%,100%)" ], + }, + "ivory": { + "hex": [ "#fffff0", "#fffffff0" ], + "func": [ "rgb(255,255,240)", "rgba(255,255,240,1)", "rgba(255,255,240,100%)", "rgb(100%,100%,94%)", "rgba(100%,100%,94%,1)", "rgba(100%,100%,94%,100%)", "hsl(60,100%,97%)", "hsla(60,100%,97%,1)", "hsla(60,100%,97%,100%)", "hwb(60,94%,0%)", "hwb(60,94%,0%,1)", "hwb(60,94%,0%,100%)" ], + }, + "khaki": { + "hex": [ "#f0e68c", "#fff0e68c" ], + "func": [ "rgb(240,230,140)", "rgba(240,230,140,1)", "rgba(240,230,140,100%)", "rgb(94%,90%,55%)", "rgba(94%,90%,55%,1)", "rgba(94%,90%,55%,100%)", "hsl(54,77%,75%)", "hsla(54,77%,75%,1)", "hsla(54,77%,75%,100%)", "hwb(54,55%,6%)", "hwb(54,55%,6%,1)", "hwb(54,55%,6%,100%)" ], + }, + "lavender": { + "hex": [ "#e6e6fa", "#ffe6e6fa" ], + "func": [ "rgb(230,230,250)", "rgba(230,230,250,1)", "rgba(230,230,250,100%)", "rgb(90%,90%,98%)", "rgba(90%,90%,98%,1)", "rgba(90%,90%,98%,100%)", "hsl(240,67%,94%)", "hsla(240,67%,94%,1)", "hsla(240,67%,94%,100%)", "hwb(240,90%,2%)", "hwb(240,90%,2%,1)", "hwb(240,90%,2%,100%)" ], + }, + "lavenderblush": { + "hex": [ "#fff0f5", "#fffff0f5" ], + "func": [ "rgb(255,240,245)", "rgba(255,240,245,1)", "rgba(255,240,245,100%)", "rgb(100%,94%,96%)", "rgba(100%,94%,96%,1)", "rgba(100%,94%,96%,100%)", "hsl(340,100%,97%)", "hsla(340,100%,97%,1)", "hsla(340,100%,97%,100%)", "hwb(340,94%,0%)", "hwb(340,94%,0%,1)", "hwb(340,94%,0%,100%)" ], + }, + "lawngreen": { + "hex": [ "#7cfc00", "#ff7cfc00" ], + "func": [ "rgb(124,252,0)", "rgba(124,252,0,1)", "rgba(124,252,0,100%)", "rgb(49%,99%,0%)", "rgba(49%,99%,0%,1)", "rgba(49%,99%,0%,100%)", "hsl(90,100%,49%)", "hsla(90,100%,49%,1)", "hsla(90,100%,49%,100%)", "hwb(90,0%,1%)", "hwb(90,0%,1%,1)", "hwb(90,0%,1%,100%)" ], + }, + "lemonchiffon": { + "hex": [ "#fffacd", "#fffffacd" ], + "func": [ "rgb(255,250,205)", "rgba(255,250,205,1)", "rgba(255,250,205,100%)", "rgb(100%,98%,80%)", "rgba(100%,98%,80%,1)", "rgba(100%,98%,80%,100%)", "hsl(54,100%,90%)", "hsla(54,100%,90%,1)", "hsla(54,100%,90%,100%)", "hwb(54,80%,0%)", "hwb(54,80%,0%,1)", "hwb(54,80%,0%,100%)" ], + }, + "lightblue": { + "hex": [ "#add8e6", "#ffadd8e6" ], + "func": [ "rgb(173,216,230)", "rgba(173,216,230,1)", "rgba(173,216,230,100%)", "rgb(68%,85%,90%)", "rgba(68%,85%,90%,1)", "rgba(68%,85%,90%,100%)", "hsl(195,53%,79%)", "hsla(195,53%,79%,1)", "hsla(195,53%,79%,100%)", "hwb(195,68%,10%)", "hwb(195,68%,10%,1)", "hwb(195,68%,10%,100%)" ], + }, + "lightcoral": { + "hex": [ "#f08080", "#fff08080" ], + "func": [ "rgb(240,128,128)", "rgba(240,128,128,1)", "rgba(240,128,128,100%)", "rgb(94%,50%,50%)", "rgba(94%,50%,50%,1)", "rgba(94%,50%,50%,100%)", "hsl(0,79%,72%)", "hsla(0,79%,72%,1)", "hsla(0,79%,72%,100%)", "hwb(0,50%,6%)", "hwb(0,50%,6%,1)", "hwb(0,50%,6%,100%)" ], + }, + "lightcyan": { + "hex": [ "#e0ffff", "#ffe0ffff" ], + "func": [ "rgb(224,255,255)", "rgba(224,255,255,1)", "rgba(224,255,255,100%)", "rgb(88%,100%,100%)", "rgba(88%,100%,100%,1)", "rgba(88%,100%,100%,100%)", "hsl(180,100%,94%)", "hsla(180,100%,94%,1)", "hsla(180,100%,94%,100%)", "hwb(180,88%,0%)", "hwb(180,88%,0%,1)", "hwb(180,88%,0%,100%)" ], + }, + "lightgoldenrodyellow": { + "hex": [ "#fafad2", "#fffafad2" ], + "func": [ "rgb(250,250,210)", "rgba(250,250,210,1)", "rgba(250,250,210,100%)", "rgb(98%,98%,82%)", "rgba(98%,98%,82%,1)", "rgba(98%,98%,82%,100%)", "hsl(60,80%,90%)", "hsla(60,80%,90%,1)", "hsla(60,80%,90%,100%)", "hwb(60,82%,2%)", "hwb(60,82%,2%,1)", "hwb(60,82%,2%,100%)" ], + }, + "lightgray": { + "hex": [ "#d3d3d3", "#ffd3d3d3" ], + "func": [ "rgb(211,211,211)", "rgba(211,211,211,1)", "rgba(211,211,211,100%)", "rgb(83%,83%,83%)", "rgba(83%,83%,83%,1)", "rgba(83%,83%,83%,100%)", "hsl(0,0%,83%)", "hsla(0,0%,83%,1)", "hsla(0,0%,83%,100%)", "hwb(0,83%,17%)", "hwb(0,83%,17%,1)", "hwb(0,83%,17%,100%)" ], + }, + "lightgreen": { + "hex": [ "#90ee90", "#ff90ee90" ], + "func": [ "rgb(144,238,144)", "rgba(144,238,144,1)", "rgba(144,238,144,100%)", "rgb(56%,93%,56%)", "rgba(56%,93%,56%,1)", "rgba(56%,93%,56%,100%)", "hsl(120,73%,75%)", "hsla(120,73%,75%,1)", "hsla(120,73%,75%,100%)", "hwb(120,56%,7%)", "hwb(120,56%,7%,1)", "hwb(120,56%,7%,100%)" ], + }, + "lightgrey": { + "hex": [ "#d3d3d3", "#ffd3d3d3" ], + "func": [ "rgb(211,211,211)", "rgba(211,211,211,1)", "rgba(211,211,211,100%)", "rgb(83%,83%,83%)", "rgba(83%,83%,83%,1)", "rgba(83%,83%,83%,100%)", "hsl(0,0%,83%)", "hsla(0,0%,83%,1)", "hsla(0,0%,83%,100%)", "hwb(0,83%,17%)", "hwb(0,83%,17%,1)", "hwb(0,83%,17%,100%)", "gray(211)", "gray(211,1)", "gray(211,100%)", "gray(211%)", "gray(211%,1)", "gray(211%,100%)" ], + }, + "lightpink": { + "hex": [ "#ffb6c1", "#ffffb6c1" ], + "func": [ "rgb(255,182,193)", "rgba(255,182,193,1)", "rgba(255,182,193,100%)", "rgb(100%,71%,76%)", "rgba(100%,71%,76%,1)", "rgba(100%,71%,76%,100%)", "hsl(351,100%,86%)", "hsla(351,100%,86%,1)", "hsla(351,100%,86%,100%)", "hwb(351,71%,0%)", "hwb(351,71%,0%,1)", "hwb(351,71%,0%,100%)" ], + }, + "lightsalmon": { + "hex": [ "#ffa07a", "#ffffa07a" ], + "func": [ "rgb(255,160,122)", "rgba(255,160,122,1)", "rgba(255,160,122,100%)", "rgb(100%,63%,48%)", "rgba(100%,63%,48%,1)", "rgba(100%,63%,48%,100%)", "hsl(17,100%,74%)", "hsla(17,100%,74%,1)", "hsla(17,100%,74%,100%)", "hwb(17,48%,0%)", "hwb(17,48%,0%,1)", "hwb(17,48%,0%,100%)" ], + }, + "lightseagreen": { + "hex": [ "#20b2aa", "#ff20b2aa" ], + "func": [ "rgb(32,178,170)", "rgba(32,178,170,1)", "rgba(32,178,170,100%)", "rgb(13%,70%,67%)", "rgba(13%,70%,67%,1)", "rgba(13%,70%,67%,100%)", "hsl(177,70%,41%)", "hsla(177,70%,41%,1)", "hsla(177,70%,41%,100%)", "hwb(177,13%,30%)", "hwb(177,13%,30%,1)", "hwb(177,13%,30%,100%)" ], + }, + "lightskyblue": { + "hex": [ "#87cefa", "#ff87cefa" ], + "func": [ "rgb(135,206,250)", "rgba(135,206,250,1)", "rgba(135,206,250,100%)", "rgb(53%,81%,98%)", "rgba(53%,81%,98%,1)", "rgba(53%,81%,98%,100%)", "hsl(203,92%,75%)", "hsla(203,92%,75%,1)", "hsla(203,92%,75%,100%)", "hwb(203,53%,2%)", "hwb(203,53%,2%,1)", "hwb(203,53%,2%,100%)" ], + }, + "lightslategray": { + "hex": [ "#778899", "#ff778899", "#789", "#f789" ], + "func": [ "rgb(119,136,153)", "rgba(119,136,153,1)", "rgba(119,136,153,100%)", "rgb(47%,53%,60%)", "rgba(47%,53%,60%,1)", "rgba(47%,53%,60%,100%)", "hsl(210,14%,53%)", "hsla(210,14%,53%,1)", "hsla(210,14%,53%,100%)", "hwb(210,47%,40%)", "hwb(210,47%,40%,1)", "hwb(210,47%,40%,100%)" ], + }, + "lightslategrey": { + "hex": [ "#778899", "#ff778899", "#789", "#f789" ], + "func": [ "rgb(119,136,153)", "rgba(119,136,153,1)", "rgba(119,136,153,100%)", "rgb(47%,53%,60%)", "rgba(47%,53%,60%,1)", "rgba(47%,53%,60%,100%)", "hsl(210,14%,53%)", "hsla(210,14%,53%,1)", "hsla(210,14%,53%,100%)", "hwb(210,47%,40%)", "hwb(210,47%,40%,1)", "hwb(210,47%,40%,100%)" ], + }, + "lightsteelblue": { + "hex": [ "#b0c4de", "#ffb0c4de" ], + "func": [ "rgb(176,196,222)", "rgba(176,196,222,1)", "rgba(176,196,222,100%)", "rgb(69%,77%,87%)", "rgba(69%,77%,87%,1)", "rgba(69%,77%,87%,100%)", "hsl(214,41%,78%)", "hsla(214,41%,78%,1)", "hsla(214,41%,78%,100%)", "hwb(214,69%,13%)", "hwb(214,69%,13%,1)", "hwb(214,69%,13%,100%)" ], + }, + "lightyellow": { + "hex": [ "#ffffe0", "#ffffffe0" ], + "func": [ "rgb(255,255,224)", "rgba(255,255,224,1)", "rgba(255,255,224,100%)", "rgb(100%,100%,88%)", "rgba(100%,100%,88%,1)", "rgba(100%,100%,88%,100%)", "hsl(60,100%,94%)", "hsla(60,100%,94%,1)", "hsla(60,100%,94%,100%)", "hwb(60,88%,0%)", "hwb(60,88%,0%,1)", "hwb(60,88%,0%,100%)" ], + }, + "lime": { + "hex": [ "#00ff00", "#ff00ff00", "#0f0", "#f0f0" ], + "func": [ "rgb(0,255,0)", "rgba(0,255,0,1)", "rgba(0,255,0,100%)", "rgb(0%,100%,0%)", "rgba(0%,100%,0%,1)", "rgba(0%,100%,0%,100%)", "hsl(120,100%,50%)", "hsla(120,100%,50%,1)", "hsla(120,100%,50%,100%)", "hwb(120,0%,0%)", "hwb(120,0%,0%,1)", "hwb(120,0%,0%,100%)" ], + }, + "limegreen": { + "hex": [ "#32cd32", "#ff32cd32" ], + "func": [ "rgb(50,205,50)", "rgba(50,205,50,1)", "rgba(50,205,50,100%)", "rgb(20%,80%,20%)", "rgba(20%,80%,20%,1)", "rgba(20%,80%,20%,100%)", "hsl(120,61%,50%)", "hsla(120,61%,50%,1)", "hsla(120,61%,50%,100%)", "hwb(120,20%,20%)", "hwb(120,20%,20%,1)", "hwb(120,20%,20%,100%)" ], + }, + "linen": { + "hex": [ "#faf0e6", "#fffaf0e6" ], + "func": [ "rgb(250,240,230)", "rgba(250,240,230,1)", "rgba(250,240,230,100%)", "rgb(98%,94%,90%)", "rgba(98%,94%,90%,1)", "rgba(98%,94%,90%,100%)", "hsl(30,67%,94%)", "hsla(30,67%,94%,1)", "hsla(30,67%,94%,100%)", "hwb(30,90%,2%)", "hwb(30,90%,2%,1)", "hwb(30,90%,2%,100%)" ], + }, + "magenta": { + "hex": [ "#ff00ff", "#ffff00ff", "#f0f", "#ff0f" ], + "func": [ "rgb(255,0,255)", "rgba(255,0,255,1)", "rgba(255,0,255,100%)", "rgb(100%,0%,100%)", "rgba(100%,0%,100%,1)", "rgba(100%,0%,100%,100%)", "hsl(300,100%,50%)", "hsla(300,100%,50%,1)", "hsla(300,100%,50%,100%)", "hwb(300,0%,0%)", "hwb(300,0%,0%,1)", "hwb(300,0%,0%,100%)" ], + }, + "maroon": { + "hex": [ "#800000", "#ff800000" ], + "func": [ "rgb(128,0,0)", "rgba(128,0,0,1)", "rgba(128,0,0,100%)", "rgb(50%,0%,0%)", "rgba(50%,0%,0%,1)", "rgba(50%,0%,0%,100%)", "hsl(0,100%,25%)", "hsla(0,100%,25%,1)", "hsla(0,100%,25%,100%)", "hwb(0,0%,50%)", "hwb(0,0%,50%,1)", "hwb(0,0%,50%,100%)" ], + }, + "mediumaquamarine": { + "hex": [ "#66cdaa", "#ff66cdaa" ], + "func": [ "rgb(102,205,170)", "rgba(102,205,170,1)", "rgba(102,205,170,100%)", "rgb(40%,80%,67%)", "rgba(40%,80%,67%,1)", "rgba(40%,80%,67%,100%)", "hsl(160,51%,60%)", "hsla(160,51%,60%,1)", "hsla(160,51%,60%,100%)", "hwb(160,40%,20%)", "hwb(160,40%,20%,1)", "hwb(160,40%,20%,100%)" ], + }, + "mediumblue": { + "hex": [ "#0000cd", "#ff0000cd" ], + "func": [ "rgb(0,0,205)", "rgba(0,0,205,1)", "rgba(0,0,205,100%)", "rgb(0%,0%,80%)", "rgba(0%,0%,80%,1)", "rgba(0%,0%,80%,100%)", "hsl(240,100%,40%)", "hsla(240,100%,40%,1)", "hsla(240,100%,40%,100%)", "hwb(240,0%,20%)", "hwb(240,0%,20%,1)", "hwb(240,0%,20%,100%)" ], + }, + "mediumorchid": { + "hex": [ "#ba55d3", "#ffba55d3" ], + "func": [ "rgb(186,85,211)", "rgba(186,85,211,1)", "rgba(186,85,211,100%)", "rgb(73%,33%,83%)", "rgba(73%,33%,83%,1)", "rgba(73%,33%,83%,100%)", "hsl(288,59%,58%)", "hsla(288,59%,58%,1)", "hsla(288,59%,58%,100%)", "hwb(288,33%,17%)", "hwb(288,33%,17%,1)", "hwb(288,33%,17%,100%)" ], + }, + "mediumpurple": { + "hex": [ "#9370db", "#ff9370db" ], + "func": [ "rgb(147,112,219)", "rgba(147,112,219,1)", "rgba(147,112,219,100%)", "rgb(58%,44%,86%)", "rgba(58%,44%,86%,1)", "rgba(58%,44%,86%,100%)", "hsl(260,60%,65%)", "hsla(260,60%,65%,1)", "hsla(260,60%,65%,100%)", "hwb(260,44%,14%)", "hwb(260,44%,14%,1)", "hwb(260,44%,14%,100%)" ], + }, + "mediumseagreen": { + "hex": [ "#3cb371", "#ff3cb371" ], + "func": [ "rgb(60,179,113)", "rgba(60,179,113,1)", "rgba(60,179,113,100%)", "rgb(24%,70%,44%)", "rgba(24%,70%,44%,1)", "rgba(24%,70%,44%,100%)", "hsl(147,50%,47%)", "hsla(147,50%,47%,1)", "hsla(147,50%,47%,100%)", "hwb(147,24%,30%)", "hwb(147,24%,30%,1)", "hwb(147,24%,30%,100%)" ], + }, + "mediumslateblue": { + "hex": [ "#7b68ee", "#ff7b68ee" ], + "func": [ "rgb(123,104,238)", "rgba(123,104,238,1)", "rgba(123,104,238,100%)", "rgb(48%,41%,93%)", "rgba(48%,41%,93%,1)", "rgba(48%,41%,93%,100%)", "hsl(249,80%,67%)", "hsla(249,80%,67%,1)", "hsla(249,80%,67%,100%)", "hwb(249,41%,7%)", "hwb(249,41%,7%,1)", "hwb(249,41%,7%,100%)" ], + }, + "mediumspringgreen": { + "hex": [ "#00fa9a", "#ff00fa9a" ], + "func": [ "rgb(0,250,154)", "rgba(0,250,154,1)", "rgba(0,250,154,100%)", "rgb(0%,98%,60%)", "rgba(0%,98%,60%,1)", "rgba(0%,98%,60%,100%)", "hsl(157,100%,49%)", "hsla(157,100%,49%,1)", "hsla(157,100%,49%,100%)", "hwb(157,0%,2%)", "hwb(157,0%,2%,1)", "hwb(157,0%,2%,100%)" ], + }, + "mediumturquoise": { + "hex": [ "#48d1cc", "#ff48d1cc" ], + "func": [ "rgb(72,209,204)", "rgba(72,209,204,1)", "rgba(72,209,204,100%)", "rgb(28%,82%,80%)", "rgba(28%,82%,80%,1)", "rgba(28%,82%,80%,100%)", "hsl(178,60%,55%)", "hsla(178,60%,55%,1)", "hsla(178,60%,55%,100%)", "hwb(178,28%,18%)", "hwb(178,28%,18%,1)", "hwb(178,28%,18%,100%)" ], + }, + "mediumvioletred": { + "hex": [ "#c71585", "#ffc71585" ], + "func": [ "rgb(199,21,133)", "rgba(199,21,133,1)", "rgba(199,21,133,100%)", "rgb(78%,8%,52%)", "rgba(78%,8%,52%,1)", "rgba(78%,8%,52%,100%)", "hsl(322,81%,43%)", "hsla(322,81%,43%,1)", "hsla(322,81%,43%,100%)", "hwb(322,8%,22%)", "hwb(322,8%,22%,1)", "hwb(322,8%,22%,100%)" ], + }, + "midnightblue": { + "hex": [ "#191970", "#ff191970" ], + "func": [ "rgb(25,25,112)", "rgba(25,25,112,1)", "rgba(25,25,112,100%)", "rgb(10%,10%,44%)", "rgba(10%,10%,44%,1)", "rgba(10%,10%,44%,100%)", "hsl(240,64%,27%)", "hsla(240,64%,27%,1)", "hsla(240,64%,27%,100%)", "hwb(240,10%,56%)", "hwb(240,10%,56%,1)", "hwb(240,10%,56%,100%)" ], + }, + "mintcream": { + "hex": [ "#f5fffa", "#fff5fffa" ], + "func": [ "rgb(245,255,250)", "rgba(245,255,250,1)", "rgba(245,255,250,100%)", "rgb(96%,100%,98%)", "rgba(96%,100%,98%,1)", "rgba(96%,100%,98%,100%)", "hsl(150,100%,98%)", "hsla(150,100%,98%,1)", "hsla(150,100%,98%,100%)", "hwb(150,96%,0%)", "hwb(150,96%,0%,1)", "hwb(150,96%,0%,100%)" ], + }, + "mistyrose": { + "hex": [ "#ffe4e1", "#ffffe4e1" ], + "func": [ "rgb(255,228,225)", "rgba(255,228,225,1)", "rgba(255,228,225,100%)", "rgb(100%,89%,88%)", "rgba(100%,89%,88%,1)", "rgba(100%,89%,88%,100%)", "hsl(6,100%,94%)", "hsla(6,100%,94%,1)", "hsla(6,100%,94%,100%)", "hwb(6,88%,0%)", "hwb(6,88%,0%,1)", "hwb(6,88%,0%,100%)" ], + }, + "moccasin": { + "hex": [ "#ffe4b5", "#ffffe4b5" ], + "func": [ "rgb(255,228,181)", "rgba(255,228,181,1)", "rgba(255,228,181,100%)", "rgb(100%,89%,71%)", "rgba(100%,89%,71%,1)", "rgba(100%,89%,71%,100%)", "hsl(38,100%,85%)", "hsla(38,100%,85%,1)", "hsla(38,100%,85%,100%)", "hwb(38,71%,0%)", "hwb(38,71%,0%,1)", "hwb(38,71%,0%,100%)" ], + }, + "navajowhite": { + "hex": [ "#ffdead", "#ffffdead" ], + "func": [ "rgb(255,222,173)", "rgba(255,222,173,1)", "rgba(255,222,173,100%)", "rgb(100%,87%,68%)", "rgba(100%,87%,68%,1)", "rgba(100%,87%,68%,100%)", "hsl(36,100%,84%)", "hsla(36,100%,84%,1)", "hsla(36,100%,84%,100%)", "hwb(36,68%,0%)", "hwb(36,68%,0%,1)", "hwb(36,68%,0%,100%)" ], + }, + "navy": { + "hex": [ "#000080", "#ff000080" ], + "func": [ "rgb(0,0,128)", "rgba(0,0,128,1)", "rgba(0,0,128,100%)", "rgb(0%,0%,50%)", "rgba(0%,0%,50%,1)", "rgba(0%,0%,50%,100%)", "hsl(240,100%,25%)", "hsla(240,100%,25%,1)", "hsla(240,100%,25%,100%)", "hwb(240,0%,50%)", "hwb(240,0%,50%,1)", "hwb(240,0%,50%,100%)" ], + }, + "oldlace": { + "hex": [ "#fdf5e6", "#fffdf5e6" ], + "func": [ "rgb(253,245,230)", "rgba(253,245,230,1)", "rgba(253,245,230,100%)", "rgb(99%,96%,90%)", "rgba(99%,96%,90%,1)", "rgba(99%,96%,90%,100%)", "hsl(39,85%,95%)", "hsla(39,85%,95%,1)", "hsla(39,85%,95%,100%)", "hwb(39,90%,1%)", "hwb(39,90%,1%,1)", "hwb(39,90%,1%,100%)" ], + }, + "olive": { + "hex": [ "#808000", "#ff808000" ], + "func": [ "rgb(128,128,0)", "rgba(128,128,0,1)", "rgba(128,128,0,100%)", "rgb(50%,50%,0%)", "rgba(50%,50%,0%,1)", "rgba(50%,50%,0%,100%)", "hsl(60,100%,25%)", "hsla(60,100%,25%,1)", "hsla(60,100%,25%,100%)", "hwb(60,0%,50%)", "hwb(60,0%,50%,1)", "hwb(60,0%,50%,100%)" ], + }, + "olivedrab": { + "hex": [ "#6b8e23", "#ff6b8e23" ], + "func": [ "rgb(107,142,35)", "rgba(107,142,35,1)", "rgba(107,142,35,100%)", "rgb(42%,56%,14%)", "rgba(42%,56%,14%,1)", "rgba(42%,56%,14%,100%)", "hsl(80,60%,35%)", "hsla(80,60%,35%,1)", "hsla(80,60%,35%,100%)", "hwb(80,14%,44%)", "hwb(80,14%,44%,1)", "hwb(80,14%,44%,100%)" ], + }, + "orange": { + "hex": [ "#ffa500", "#ffffa500" ], + "func": [ "rgb(255,165,0)", "rgba(255,165,0,1)", "rgba(255,165,0,100%)", "rgb(100%,65%,0%)", "rgba(100%,65%,0%,1)", "rgba(100%,65%,0%,100%)", "hsl(39,100%,50%)", "hsla(39,100%,50%,1)", "hsla(39,100%,50%,100%)", "hwb(39,0%,0%)", "hwb(39,0%,0%,1)", "hwb(39,0%,0%,100%)" ], + }, + "orangered": { + "hex": [ "#ff4500", "#ffff4500" ], + "func": [ "rgb(255,69,0)", "rgba(255,69,0,1)", "rgba(255,69,0,100%)", "rgb(100%,27%,0%)", "rgba(100%,27%,0%,1)", "rgba(100%,27%,0%,100%)", "hsl(16,100%,50%)", "hsla(16,100%,50%,1)", "hsla(16,100%,50%,100%)", "hwb(16,0%,0%)", "hwb(16,0%,0%,1)", "hwb(16,0%,0%,100%)" ], + }, + "orchid": { + "hex": [ "#da70d6", "#ffda70d6" ], + "func": [ "rgb(218,112,214)", "rgba(218,112,214,1)", "rgba(218,112,214,100%)", "rgb(85%,44%,84%)", "rgba(85%,44%,84%,1)", "rgba(85%,44%,84%,100%)", "hsl(302,59%,65%)", "hsla(302,59%,65%,1)", "hsla(302,59%,65%,100%)", "hwb(302,44%,15%)", "hwb(302,44%,15%,1)", "hwb(302,44%,15%,100%)" ], + }, + "palegoldenrod": { + "hex": [ "#eee8aa", "#ffeee8aa" ], + "func": [ "rgb(238,232,170)", "rgba(238,232,170,1)", "rgba(238,232,170,100%)", "rgb(93%,91%,67%)", "rgba(93%,91%,67%,1)", "rgba(93%,91%,67%,100%)", "hsl(55,67%,80%)", "hsla(55,67%,80%,1)", "hsla(55,67%,80%,100%)", "hwb(55,67%,7%)", "hwb(55,67%,7%,1)", "hwb(55,67%,7%,100%)" ], + }, + "palegreen": { + "hex": [ "#98fb98", "#ff98fb98" ], + "func": [ "rgb(152,251,152)", "rgba(152,251,152,1)", "rgba(152,251,152,100%)", "rgb(60%,98%,60%)", "rgba(60%,98%,60%,1)", "rgba(60%,98%,60%,100%)", "hsl(120,93%,79%)", "hsla(120,93%,79%,1)", "hsla(120,93%,79%,100%)", "hwb(120,60%,2%)", "hwb(120,60%,2%,1)", "hwb(120,60%,2%,100%)" ], + }, + "paleturquoise": { + "hex": [ "#afeeee", "#ffafeeee" ], + "func": [ "rgb(175,238,238)", "rgba(175,238,238,1)", "rgba(175,238,238,100%)", "rgb(69%,93%,93%)", "rgba(69%,93%,93%,1)", "rgba(69%,93%,93%,100%)", "hsl(180,65%,81%)", "hsla(180,65%,81%,1)", "hsla(180,65%,81%,100%)", "hwb(180,69%,7%)", "hwb(180,69%,7%,1)", "hwb(180,69%,7%,100%)" ], + }, + "palevioletred": { + "hex": [ "#db7093", "#ffdb7093" ], + "func": [ "rgb(219,112,147)", "rgba(219,112,147,1)", "rgba(219,112,147,100%)", "rgb(86%,44%,58%)", "rgba(86%,44%,58%,1)", "rgba(86%,44%,58%,100%)", "hsl(340,60%,65%)", "hsla(340,60%,65%,1)", "hsla(340,60%,65%,100%)", "hwb(340,44%,14%)", "hwb(340,44%,14%,1)", "hwb(340,44%,14%,100%)" ], + }, + "papayawhip": { + "hex": [ "#ffefd5", "#ffffefd5" ], + "func": [ "rgb(255,239,213)", "rgba(255,239,213,1)", "rgba(255,239,213,100%)", "rgb(100%,94%,84%)", "rgba(100%,94%,84%,1)", "rgba(100%,94%,84%,100%)", "hsl(37,100%,92%)", "hsla(37,100%,92%,1)", "hsla(37,100%,92%,100%)", "hwb(37,84%,0%)", "hwb(37,84%,0%,1)", "hwb(37,84%,0%,100%)" ], + }, + "peachpuff": { + "hex": [ "#ffdab9", "#ffffdab9" ], + "func": [ "rgb(255,218,185)", "rgba(255,218,185,1)", "rgba(255,218,185,100%)", "rgb(100%,85%,73%)", "rgba(100%,85%,73%,1)", "rgba(100%,85%,73%,100%)", "hsl(28,100%,86%)", "hsla(28,100%,86%,1)", "hsla(28,100%,86%,100%)", "hwb(28,73%,0%)", "hwb(28,73%,0%,1)", "hwb(28,73%,0%,100%)" ], + }, + "peru": { + "hex": [ "#cd853f", "#ffcd853f" ], + "func": [ "rgb(205,133,63)", "rgba(205,133,63,1)", "rgba(205,133,63,100%)", "rgb(80%,52%,25%)", "rgba(80%,52%,25%,1)", "rgba(80%,52%,25%,100%)", "hsl(30,59%,53%)", "hsla(30,59%,53%,1)", "hsla(30,59%,53%,100%)", "hwb(30,25%,20%)", "hwb(30,25%,20%,1)", "hwb(30,25%,20%,100%)" ], + }, + "pink": { + "hex": [ "#ffc0cb", "#ffffc0cb" ], + "func": [ "rgb(255,192,203)", "rgba(255,192,203,1)", "rgba(255,192,203,100%)", "rgb(100%,75%,80%)", "rgba(100%,75%,80%,1)", "rgba(100%,75%,80%,100%)", "hsl(350,100%,88%)", "hsla(350,100%,88%,1)", "hsla(350,100%,88%,100%)", "hwb(350,75%,0%)", "hwb(350,75%,0%,1)", "hwb(350,75%,0%,100%)" ], + }, + "plum": { + "hex": [ "#dda0dd", "#ffdda0dd" ], + "func": [ "rgb(221,160,221)", "rgba(221,160,221,1)", "rgba(221,160,221,100%)", "rgb(87%,63%,87%)", "rgba(87%,63%,87%,1)", "rgba(87%,63%,87%,100%)", "hsl(300,47%,75%)", "hsla(300,47%,75%,1)", "hsla(300,47%,75%,100%)", "hwb(300,63%,13%)", "hwb(300,63%,13%,1)", "hwb(300,63%,13%,100%)" ], + }, + "powderblue": { + "hex": [ "#b0e0e6", "#ffb0e0e6" ], + "func": [ "rgb(176,224,230)", "rgba(176,224,230,1)", "rgba(176,224,230,100%)", "rgb(69%,88%,90%)", "rgba(69%,88%,90%,1)", "rgba(69%,88%,90%,100%)", "hsl(187,52%,80%)", "hsla(187,52%,80%,1)", "hsla(187,52%,80%,100%)", "hwb(187,69%,10%)", "hwb(187,69%,10%,1)", "hwb(187,69%,10%,100%)" ], + }, + "purple": { + "hex": [ "#800080", "#ff800080" ], + "func": [ "rgb(128,0,128)", "rgba(128,0,128,1)", "rgba(128,0,128,100%)", "rgb(50%,0%,50%)", "rgba(50%,0%,50%,1)", "rgba(50%,0%,50%,100%)", "hsl(300,100%,25%)", "hsla(300,100%,25%,1)", "hsla(300,100%,25%,100%)", "hwb(300,0%,50%)", "hwb(300,0%,50%,1)", "hwb(300,0%,50%,100%)" ], + }, + "rebeccapurple": { + "hex": [ "#663399", "#ff663399", "#639", "#f639" ], + "func": [ "rgb(102,51,153)", "rgba(102,51,153,1)", "rgba(102,51,153,100%)", "rgb(40%,20%,60%)", "rgba(40%,20%,60%,1)", "rgba(40%,20%,60%,100%)", "hsl(270,50%,40%)", "hsla(270,50%,40%,1)", "hsla(270,50%,40%,100%)", "hwb(270,20%,40%)", "hwb(270,20%,40%,1)", "hwb(270,20%,40%,100%)" ], + }, + "red": { + "hex": [ "#ff0000", "#ffff0000", "#f00", "#ff00" ], + "func": [ "rgb(255,0,0)", "rgba(255,0,0,1)", "rgba(255,0,0,100%)", "rgb(100%,0%,0%)", "rgba(100%,0%,0%,1)", "rgba(100%,0%,0%,100%)", "hsl(0,100%,50%)", "hsla(0,100%,50%,1)", "hsla(0,100%,50%,100%)", "hwb(0,0%,0%)", "hwb(0,0%,0%,1)", "hwb(0,0%,0%,100%)" ], + }, + "rosybrown": { + "hex": [ "#bc8f8f", "#ffbc8f8f" ], + "func": [ "rgb(188,143,143)", "rgba(188,143,143,1)", "rgba(188,143,143,100%)", "rgb(74%,56%,56%)", "rgba(74%,56%,56%,1)", "rgba(74%,56%,56%,100%)", "hsl(0,25%,65%)", "hsla(0,25%,65%,1)", "hsla(0,25%,65%,100%)", "hwb(0,56%,26%)", "hwb(0,56%,26%,1)", "hwb(0,56%,26%,100%)" ], + }, + "royalblue": { + "hex": [ "#4169e1", "#ff4169e1" ], + "func": [ "rgb(65,105,225)", "rgba(65,105,225,1)", "rgba(65,105,225,100%)", "rgb(25%,41%,88%)", "rgba(25%,41%,88%,1)", "rgba(25%,41%,88%,100%)", "hsl(225,73%,57%)", "hsla(225,73%,57%,1)", "hsla(225,73%,57%,100%)", "hwb(225,25%,12%)", "hwb(225,25%,12%,1)", "hwb(225,25%,12%,100%)" ], + }, + "saddlebrown": { + "hex": [ "#8b4513", "#ff8b4513" ], + "func": [ "rgb(139,69,19)", "rgba(139,69,19,1)", "rgba(139,69,19,100%)", "rgb(55%,27%,7%)", "rgba(55%,27%,7%,1)", "rgba(55%,27%,7%,100%)", "hsl(25,76%,31%)", "hsla(25,76%,31%,1)", "hsla(25,76%,31%,100%)", "hwb(25,7%,45%)", "hwb(25,7%,45%,1)", "hwb(25,7%,45%,100%)" ], + }, + "salmon": { + "hex": [ "#fa8072", "#fffa8072" ], + "func": [ "rgb(250,128,114)", "rgba(250,128,114,1)", "rgba(250,128,114,100%)", "rgb(98%,50%,45%)", "rgba(98%,50%,45%,1)", "rgba(98%,50%,45%,100%)", "hsl(6,93%,71%)", "hsla(6,93%,71%,1)", "hsla(6,93%,71%,100%)", "hwb(6,45%,2%)", "hwb(6,45%,2%,1)", "hwb(6,45%,2%,100%)" ], + }, + "sandybrown": { + "hex": [ "#f4a460", "#fff4a460" ], + "func": [ "rgb(244,164,96)", "rgba(244,164,96,1)", "rgba(244,164,96,100%)", "rgb(96%,64%,38%)", "rgba(96%,64%,38%,1)", "rgba(96%,64%,38%,100%)", "hsl(28,87%,67%)", "hsla(28,87%,67%,1)", "hsla(28,87%,67%,100%)", "hwb(28,38%,4%)", "hwb(28,38%,4%,1)", "hwb(28,38%,4%,100%)" ], + }, + "seagreen": { + "hex": [ "#2e8b57", "#ff2e8b57" ], + "func": [ "rgb(46,139,87)", "rgba(46,139,87,1)", "rgba(46,139,87,100%)", "rgb(18%,55%,34%)", "rgba(18%,55%,34%,1)", "rgba(18%,55%,34%,100%)", "hsl(146,50%,36%)", "hsla(146,50%,36%,1)", "hsla(146,50%,36%,100%)", "hwb(146,18%,45%)", "hwb(146,18%,45%,1)", "hwb(146,18%,45%,100%)" ], + }, + "seashell": { + "hex": [ "#fff5ee", "#fffff5ee" ], + "func": [ "rgb(255,245,238)", "rgba(255,245,238,1)", "rgba(255,245,238,100%)", "rgb(100%,96%,93%)", "rgba(100%,96%,93%,1)", "rgba(100%,96%,93%,100%)", "hsl(25,100%,97%)", "hsla(25,100%,97%,1)", "hsla(25,100%,97%,100%)", "hwb(25,93%,0%)", "hwb(25,93%,0%,1)", "hwb(25,93%,0%,100%)" ], + }, + "sienna": { + "hex": [ "#a0522d", "#ffa0522d" ], + "func": [ "rgb(160,82,45)", "rgba(160,82,45,1)", "rgba(160,82,45,100%)", "rgb(63%,32%,18%)", "rgba(63%,32%,18%,1)", "rgba(63%,32%,18%,100%)", "hsl(19,56%,40%)", "hsla(19,56%,40%,1)", "hsla(19,56%,40%,100%)", "hwb(19,18%,37%)", "hwb(19,18%,37%,1)", "hwb(19,18%,37%,100%)" ], + }, + "silver": { + "hex": [ "#c0c0c0", "#ffc0c0c0" ], + "func": [ "rgb(192,192,192)", "rgba(192,192,192,1)", "rgba(192,192,192,100%)", "rgb(75%,75%,75%)", "rgba(75%,75%,75%,1)", "rgba(75%,75%,75%,100%)", "hsl(0,0%,75%)", "hsla(0,0%,75%,1)", "hsla(0,0%,75%,100%)", "hwb(0,75%,25%)", "hwb(0,75%,25%,1)", "hwb(0,75%,25%,100%)" ], + }, + "skyblue": { + "hex": [ "#87ceeb", "#ff87ceeb" ], + "func": [ "rgb(135,206,235)", "rgba(135,206,235,1)", "rgba(135,206,235,100%)", "rgb(53%,81%,92%)", "rgba(53%,81%,92%,1)", "rgba(53%,81%,92%,100%)", "hsl(197,71%,73%)", "hsla(197,71%,73%,1)", "hsla(197,71%,73%,100%)", "hwb(197,53%,8%)", "hwb(197,53%,8%,1)", "hwb(197,53%,8%,100%)" ], + }, + "slateblue": { + "hex": [ "#6a5acd", "#ff6a5acd" ], + "func": [ "rgb(106,90,205)", "rgba(106,90,205,1)", "rgba(106,90,205,100%)", "rgb(42%,35%,80%)", "rgba(42%,35%,80%,1)", "rgba(42%,35%,80%,100%)", "hsl(248,53%,58%)", "hsla(248,53%,58%,1)", "hsla(248,53%,58%,100%)", "hwb(248,35%,20%)", "hwb(248,35%,20%,1)", "hwb(248,35%,20%,100%)" ], + }, + "slategray": { + "hex": [ "#708090", "#ff708090" ], + "func": [ "rgb(112,128,144)", "rgba(112,128,144,1)", "rgba(112,128,144,100%)", "rgb(44%,50%,56%)", "rgba(44%,50%,56%,1)", "rgba(44%,50%,56%,100%)", "hsl(210,13%,50%)", "hsla(210,13%,50%,1)", "hsla(210,13%,50%,100%)", "hwb(210,44%,44%)", "hwb(210,44%,44%,1)", "hwb(210,44%,44%,100%)" ], + }, + "slategrey": { + "hex": [ "#708090", "#ff708090" ], + "func": [ "rgb(112,128,144)", "rgba(112,128,144,1)", "rgba(112,128,144,100%)", "rgb(44%,50%,56%)", "rgba(44%,50%,56%,1)", "rgba(44%,50%,56%,100%)", "hsl(210,13%,50%)", "hsla(210,13%,50%,1)", "hsla(210,13%,50%,100%)", "hwb(210,44%,44%)", "hwb(210,44%,44%,1)", "hwb(210,44%,44%,100%)" ], + }, + "snow": { + "hex": [ "#fffafa", "#fffffafa" ], + "func": [ "rgb(255,250,250)", "rgba(255,250,250,1)", "rgba(255,250,250,100%)", "rgb(100%,98%,98%)", "rgba(100%,98%,98%,1)", "rgba(100%,98%,98%,100%)", "hsl(0,100%,99%)", "hsla(0,100%,99%,1)", "hsla(0,100%,99%,100%)", "hwb(0,98%,0%)", "hwb(0,98%,0%,1)", "hwb(0,98%,0%,100%)" ], + }, + "springgreen": { + "hex": [ "#00ff7f", "#ff00ff7f" ], + "func": [ "rgb(0,255,127)", "rgba(0,255,127,1)", "rgba(0,255,127,100%)", "rgb(0%,100%,50%)", "rgba(0%,100%,50%,1)", "rgba(0%,100%,50%,100%)", "hsl(150,100%,50%)", "hsla(150,100%,50%,1)", "hsla(150,100%,50%,100%)", "hwb(150,0%,0%)", "hwb(150,0%,0%,1)", "hwb(150,0%,0%,100%)" ], + }, + "steelblue": { + "hex": [ "#4682b4", "#ff4682b4" ], + "func": [ "rgb(70,130,180)", "rgba(70,130,180,1)", "rgba(70,130,180,100%)", "rgb(27%,51%,71%)", "rgba(27%,51%,71%,1)", "rgba(27%,51%,71%,100%)", "hsl(207,44%,49%)", "hsla(207,44%,49%,1)", "hsla(207,44%,49%,100%)", "hwb(207,27%,29%)", "hwb(207,27%,29%,1)", "hwb(207,27%,29%,100%)" ], + }, + "tan": { + "hex": [ "#d2b48c", "#ffd2b48c" ], + "func": [ "rgb(210,180,140)", "rgba(210,180,140,1)", "rgba(210,180,140,100%)", "rgb(82%,71%,55%)", "rgba(82%,71%,55%,1)", "rgba(82%,71%,55%,100%)", "hsl(34,44%,69%)", "hsla(34,44%,69%,1)", "hsla(34,44%,69%,100%)", "hwb(34,55%,18%)", "hwb(34,55%,18%,1)", "hwb(34,55%,18%,100%)" ], + }, + "teal": { + "hex": [ "#008080", "#ff008080" ], + "func": [ "rgb(0,128,128)", "rgba(0,128,128,1)", "rgba(0,128,128,100%)", "rgb(0%,50%,50%)", "rgba(0%,50%,50%,1)", "rgba(0%,50%,50%,100%)", "hsl(180,100%,25%)", "hsla(180,100%,25%,1)", "hsla(180,100%,25%,100%)", "hwb(180,0%,50%)", "hwb(180,0%,50%,1)", "hwb(180,0%,50%,100%)" ], + }, + "thistle": { + "hex": [ "#d8bfd8", "#ffd8bfd8" ], + "func": [ "rgb(216,191,216)", "rgba(216,191,216,1)", "rgba(216,191,216,100%)", "rgb(85%,75%,85%)", "rgba(85%,75%,85%,1)", "rgba(85%,75%,85%,100%)", "hsl(300,24%,80%)", "hsla(300,24%,80%,1)", "hsla(300,24%,80%,100%)", "hwb(300,75%,15%)", "hwb(300,75%,15%,1)", "hwb(300,75%,15%,100%)" ], + }, + "tomato": { + "hex": [ "#ff6347", "#ffff6347" ], + "func": [ "rgb(255,99,71)", "rgba(255,99,71,1)", "rgba(255,99,71,100%)", "rgb(100%,39%,28%)", "rgba(100%,39%,28%,1)", "rgba(100%,39%,28%,100%)", "hsl(9,100%,64%)", "hsla(9,100%,64%,1)", "hsla(9,100%,64%,100%)", "hwb(9,28%,0%)", "hwb(9,28%,0%,1)", "hwb(9,28%,0%,100%)" ], + }, + "turquoise": { + "hex": [ "#40e0d0", "#ff40e0d0" ], + "func": [ "rgb(64,224,208)", "rgba(64,224,208,1)", "rgba(64,224,208,100%)", "rgb(25%,88%,82%)", "rgba(25%,88%,82%,1)", "rgba(25%,88%,82%,100%)", "hsl(174,72%,56%)", "hsla(174,72%,56%,1)", "hsla(174,72%,56%,100%)", "hwb(174,25%,12%)", "hwb(174,25%,12%,1)", "hwb(174,25%,12%,100%)" ], + }, + "violet": { + "hex": [ "#ee82ee", "#ffee82ee" ], + "func": [ "rgb(238,130,238)", "rgba(238,130,238,1)", "rgba(238,130,238,100%)", "rgb(93%,51%,93%)", "rgba(93%,51%,93%,1)", "rgba(93%,51%,93%,100%)", "hsl(300,76%,72%)", "hsla(300,76%,72%,1)", "hsla(300,76%,72%,100%)", "hwb(300,51%,7%)", "hwb(300,51%,7%,1)", "hwb(300,51%,7%,100%)" ], + }, + "wheat": { + "hex": [ "#f5deb3", "#fff5deb3" ], + "func": [ "rgb(245,222,179)", "rgba(245,222,179,1)", "rgba(245,222,179,100%)", "rgb(96%,87%,70%)", "rgba(96%,87%,70%,1)", "rgba(96%,87%,70%,100%)", "hsl(39,77%,83%)", "hsla(39,77%,83%,1)", "hsla(39,77%,83%,100%)", "hwb(39,70%,4%)", "hwb(39,70%,4%,1)", "hwb(39,70%,4%,100%)" ], + }, + "white": { + "hex": [ "#ffffff", "#ffffffff", "#fff", "#ffff" ], + "func": [ "rgb(255,255,255)", "rgba(255,255,255,1)", "rgba(255,255,255,100%)", "rgb(100%,100%,100%)", "rgba(100%,100%,100%,1)", "rgba(100%,100%,100%,100%)", "hsl(0,0%,100%)", "hsla(0,0%,100%,1)", "hsla(0,0%,100%,100%)", "hwb(0,100%,0%)", "hwb(0,100%,0%,1)", "hwb(0,100%,0%,100%)", "gray(255)", "gray(255,1)", "gray(255,100%)", "gray(255%)", "gray(255%,1)", "gray(255%,100%)" ], + }, + "whitesmoke": { + "hex": [ "#f5f5f5", "#fff5f5f5" ], + "func": [ "rgb(245,245,245)", "rgba(245,245,245,1)", "rgba(245,245,245,100%)", "rgb(96%,96%,96%)", "rgba(96%,96%,96%,1)", "rgba(96%,96%,96%,100%)", "hsl(0,0%,96%)", "hsla(0,0%,96%,1)", "hsla(0,0%,96%,100%)", "hwb(0,96%,4%)", "hwb(0,96%,4%,1)", "hwb(0,96%,4%,100%)", "gray(245)", "gray(245,1)", "gray(245,100%)", "gray(245%)", "gray(245%,1)", "gray(245%,100%)" ], + }, + "yellow": { + "hex": [ "#ffff00", "#ffffff00", "#ff0", "#fff0" ], + "func": [ "rgb(255,255,0)", "rgba(255,255,0,1)", "rgba(255,255,0,100%)", "rgb(100%,100%,0%)", "rgba(100%,100%,0%,1)", "rgba(100%,100%,0%,100%)", "hsl(60,100%,50%)", "hsla(60,100%,50%,1)", "hsla(60,100%,50%,100%)", "hwb(60,0%,0%)", "hwb(60,0%,0%,1)", "hwb(60,0%,0%,100%)" ], + }, + "yellowgreen": { + "hex": [ "#9acd32", "#ff9acd32" ], + "func": [ "rgb(154,205,50)", "rgba(154,205,50,1)", "rgba(154,205,50,100%)", "rgb(60%,80%,20%)", "rgba(60%,80%,20%,1)", "rgba(60%,80%,20%,100%)", "hsl(80,61%,50%)", "hsla(80,61%,50%,1)", "hsla(80,61%,50%,100%)", "hwb(80,20%,20%)", "hwb(80,20%,20%,1)", "hwb(80,20%,20%,100%)" ], + }, +} diff --git a/lib/reference/propertySets.js b/lib/reference/propertySets.js new file mode 100644 index 0000000000..14966285eb --- /dev/null +++ b/lib/reference/propertySets.js @@ -0,0 +1,7 @@ +"use strict" + +const propertySets = {} + +propertySets.acceptCustomIdents = new Set([ "animation", "animation-name", "font", "font-family", "counter-increment", "grid-row", "grid-column", "grid-area", "list-style", "list-style-type" ]) + +module.exports = propertySets diff --git a/lib/reference/punctuationSets.js b/lib/reference/punctuationSets.js new file mode 100644 index 0000000000..45616e9161 --- /dev/null +++ b/lib/reference/punctuationSets.js @@ -0,0 +1,9 @@ +"use strict" + +const punctuationSets = {} + +punctuationSets.mediaFeaturePunctuation = new Set([ ":", "=", ">", ">=", "<", "<=" ]) + +punctuationSets.nonSpaceCombinators = new Set([ ">", "+", "~" ]) + +module.exports = punctuationSets diff --git a/lib/reference/shorthandData.js b/lib/reference/shorthandData.js new file mode 100644 index 0000000000..1cac1734df --- /dev/null +++ b/lib/reference/shorthandData.js @@ -0,0 +1,22 @@ +"use strict" + +module.exports = { + "margin": [ "margin-top", "margin-bottom", "margin-left", "margin-right" ], + "padding": [ "padding-top", "padding-bottom", "padding-left", "padding-right" ], + "background": [ "background-image", "background-size", "background-position", "background-repeat", "background-origin", "background-clip", "background-attachment", "background-color" ], + "font": [ "font-style", "font-variant", "font-weight", "font-stretch", "font-size", "font-family", "line-height" ], + "border": [ "border-top-width", "border-bottom-width", "border-left-width", "border-right-width", "border-top-style", "border-bottom-style", "border-left-style", "border-right-style", "border-top-color", "border-bottom-color", "border-left-color", "border-right-color" ], + "border-top": [ "border-top-width", "border-top-style", "border-top-color" ], + "border-bottom": [ "border-bottom-width", "border-bottom-style", "border-bottom-color" ], + "border-left": [ "border-left-width", "border-left-style", "border-left-color" ], + "border-right": [ "border-right-width", "border-right-style", "border-right-color" ], + "border-width": [ "border-top-width", "border-bottom-width", "border-left-width", "border-right-width" ], + "border-style": [ "border-top-style", "border-bottom-style", "border-left-style", "border-right-style" ], + "border-color": [ "border-top-color", "border-bottom-color", "border-left-color", "border-right-color" ], + "list-style": [ "list-style-type", "list-style-position", "list-style-image" ], + "border-radius": [ "border-top-right-radius", "border-top-left-radius", "border-bottom-right-radius", "border-bottom-left-radius" ], + "transition": [ "transition-delay", "transition-duration", "transition-property", "transition-timing-function" ], + "-webkit-transition": [ "-webkit-transition-delay", "-webkit-transition-duration", "-webkit-transition-property", "-webkit-transition-timing-function" ], + "-moz-transition": [ "-moz-transition-delay", "-moz-transition-duration", "-moz-transition-property", "-moz-transition-timing-function" ], + "-o-transition": [ "-o-transition-delay", "-o-transition-duration", "-o-transition-property", "-o-transition-timing-function" ], +} diff --git a/src/rules/at-rule-blacklist/README.md b/lib/rules/at-rule-blacklist/README.md similarity index 100% rename from src/rules/at-rule-blacklist/README.md rename to lib/rules/at-rule-blacklist/README.md diff --git a/src/rules/at-rule-blacklist/__tests__/index.js b/lib/rules/at-rule-blacklist/__tests__/index.js similarity index 92% rename from src/rules/at-rule-blacklist/__tests__/index.js rename to lib/rules/at-rule-blacklist/__tests__/index.js index 91fe57d8d7..415afb5b87 100644 --- a/src/rules/at-rule-blacklist/__tests__/index.js +++ b/lib/rules/at-rule-blacklist/__tests__/index.js @@ -1,20 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "extend", - "supports", - "keyframes", - ]], + config: [[ "extend", "supports", "keyframes" ]], accept: [ { code: "a { color: pink; }", diff --git a/lib/rules/at-rule-blacklist/index.js b/lib/rules/at-rule-blacklist/index.js new file mode 100644 index 0000000000..7ff14ef15b --- /dev/null +++ b/lib/rules/at-rule-blacklist/index.js @@ -0,0 +1,48 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "at-rule-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: name => `Unexpected at-rule "${name}"`, +}) + +const rule = function (blacklistInput) { + // To allow for just a string as a parameter (not only arrays of strings) + const blacklist = [].concat(blacklistInput) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + const name = atRule.name + + if (blacklist.indexOf(postcss.vendor.unprefixed(name).toLowerCase()) === -1) { + return + } + + report({ + message: messages.rejected(name), + node: atRule, + result, + ruleName, + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/at-rule-empty-line-before/README.md b/lib/rules/at-rule-empty-line-before/README.md similarity index 100% rename from src/rules/at-rule-empty-line-before/README.md rename to lib/rules/at-rule-empty-line-before/README.md diff --git a/src/rules/at-rule-empty-line-before/__tests__/index.js b/lib/rules/at-rule-empty-line-before/__tests__/index.js similarity index 97% rename from src/rules/at-rule-empty-line-before/__tests__/index.js rename to lib/rules/at-rule-empty-line-before/__tests__/index.js index da564924a8..7ec2f0ae6e 100644 --- a/src/rules/at-rule-empty-line-before/__tests__/index.js +++ b/lib/rules/at-rule-empty-line-before/__tests__/index.js @@ -1,13 +1,11 @@ -import { - mergeTestDescriptions, - testRule, -} from "../../../testUtils" -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { stripIndent } from "common-tags" +"use strict" + +const mergeTestDescriptions = require("../../../testUtils/mergeTestDescriptions") +const testRule = require("../../../testUtils/testRule") +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const stripIndent = require("common-tags").stripIndent const rule = rules[ruleName] diff --git a/lib/rules/at-rule-empty-line-before/index.js b/lib/rules/at-rule-empty-line-before/index.js new file mode 100644 index 0000000000..9879620ec6 --- /dev/null +++ b/lib/rules/at-rule-empty-line-before/index.js @@ -0,0 +1,114 @@ +"use strict" + +const hasBlock = require("../../utils/hasBlock") +const hasEmptyLine = require("../../utils/hasEmptyLine") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") + +const ruleName = "at-rule-empty-line-before" + +const messages = ruleMessages(ruleName, { + expected: "Expected empty line before at-rule", + rejected: "Unexpected empty line before at-rule", +}) + +const rule = function (expectation, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }, { + actual: options, + possible: { + except: [ "all-nested", "blockless-after-same-name-blockless", "blockless-group", "first-nested" ], + ignore: [ "after-comment", "all-nested", "blockless-after-same-name-blockless", "blockless-group" ], + ignoreAtRules: [_.isString], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + // Ignore the first node + if (atRule === root.first) { + return + } + + // Return early if at-rule is to be ignored + if (optionsMatches(options, "ignoreAtRules", atRule.name)) { + return + } + + // Optionally ignore the expectation if the node is blockless + if (optionsMatches(options, "ignore", "blockless-group") && !hasBlock(atRule)) { + return + } + + const isNested = atRule.parent !== root + const previousNode = atRule.prev() + + // Optionally ignore the expection if the node is blockless + // and following another blockless at-rule with the same name + if (optionsMatches(options, "ignore", "blockless-after-same-name-blockless") && isBlocklessAfterSameNameBlockless()) { + return + } + + // Optionally ignore the expectation if the node is nested + if (optionsMatches(options, "ignore", "all-nested") && isNested) { + return + } + + // Optionally ignore the expectation if a comment precedes this node + if (optionsMatches(options, "ignore", "after-comment") && isAfterComment()) { + return + } + + const hasEmptyLineBefore = hasEmptyLine(atRule.raws.before) + let expectEmptyLineBefore = expectation === "always" ? true : false + + // Optionally reverse the expectation if any exceptions apply + if (optionsMatches(options, "except", "all-nested") && isNested || optionsMatches(options, "except", "first-nested") && isFirstNested() || optionsMatches(options, "except", "blockless-group") && isBlocklessAfterBlockless() || optionsMatches(options, "except", "blockless-after-same-name-blockless") && isBlocklessAfterSameNameBlockless()) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Return if the expectation is met + if (expectEmptyLineBefore === hasEmptyLineBefore) { + return + } + + const message = expectEmptyLineBefore ? messages.expected : messages.rejected + + report({ + message, + node: atRule, + result, + ruleName, + }) + + function isAfterComment() { + return previousNode && previousNode.type === "comment" + } + + function isBlocklessAfterBlockless() { + return previousNode && previousNode.type === "atrule" && !hasBlock(previousNode) && !hasBlock(atRule) + } + + function isBlocklessAfterSameNameBlockless() { + return !hasBlock(atRule) && previousNode && !hasBlock(previousNode) && previousNode.type === "atrule" && previousNode.name == atRule.name + } + + function isFirstNested() { + return isNested && atRule === atRule.parent.first + } + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/at-rule-name-case/README.md b/lib/rules/at-rule-name-case/README.md similarity index 100% rename from src/rules/at-rule-name-case/README.md rename to lib/rules/at-rule-name-case/README.md diff --git a/src/rules/at-rule-name-case/__tests__/index.js b/lib/rules/at-rule-name-case/__tests__/index.js similarity index 96% rename from src/rules/at-rule-name-case/__tests__/index.js rename to lib/rules/at-rule-name-case/__tests__/index.js index cbff6497be..66462082f8 100644 --- a/src/rules/at-rule-name-case/__tests__/index.js +++ b/lib/rules/at-rule-name-case/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/at-rule-name-case/index.js b/lib/rules/at-rule-name-case/index.js new file mode 100644 index 0000000000..09d9521abb --- /dev/null +++ b/lib/rules/at-rule-name-case/index.js @@ -0,0 +1,44 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "at-rule-name-case" + +const messages = ruleMessages(ruleName, { + expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "lower", "upper" ], + }) + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + const name = atRule.name + + const expectedName = expectation === "lower" ? name.toLowerCase() : name.toUpperCase() + + if (name === expectedName) { + return + } + + report({ + message: messages.expected(name, expectedName), + node: atRule, + ruleName, + result, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/at-rule-name-newline-after/README.md b/lib/rules/at-rule-name-newline-after/README.md similarity index 100% rename from src/rules/at-rule-name-newline-after/README.md rename to lib/rules/at-rule-name-newline-after/README.md diff --git a/src/rules/at-rule-name-newline-after/__tests__/index.js b/lib/rules/at-rule-name-newline-after/__tests__/index.js similarity index 97% rename from src/rules/at-rule-name-newline-after/__tests__/index.js rename to lib/rules/at-rule-name-newline-after/__tests__/index.js index 745353e468..e3a74aa0e7 100644 --- a/src/rules/at-rule-name-newline-after/__tests__/index.js +++ b/lib/rules/at-rule-name-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/at-rule-name-newline-after/index.js b/lib/rules/at-rule-name-newline-after/index.js new file mode 100644 index 0000000000..a58a39e374 --- /dev/null +++ b/lib/rules/at-rule-name-newline-after/index.js @@ -0,0 +1,36 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const atRuleNameSpaceChecker = require("../atRuleNameSpaceChecker") + +const ruleName = "at-rule-name-newline-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: name => `Expected newline after at-rule name \"${name}\"`, +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("newline", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "always-multi-line" ], + }) + if (!validOptions) { + return + } + + atRuleNameSpaceChecker({ + root, + result, + locationChecker: checker.afterOneOnly, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/at-rule-name-space-after/README.md b/lib/rules/at-rule-name-space-after/README.md similarity index 100% rename from src/rules/at-rule-name-space-after/README.md rename to lib/rules/at-rule-name-space-after/README.md diff --git a/src/rules/at-rule-name-space-after/__tests__/index.js b/lib/rules/at-rule-name-space-after/__tests__/index.js similarity index 97% rename from src/rules/at-rule-name-space-after/__tests__/index.js rename to lib/rules/at-rule-name-space-after/__tests__/index.js index 523f02228a..a3b76f4928 100644 --- a/src/rules/at-rule-name-space-after/__tests__/index.js +++ b/lib/rules/at-rule-name-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/at-rule-name-space-after/index.js b/lib/rules/at-rule-name-space-after/index.js new file mode 100644 index 0000000000..7dcfd68355 --- /dev/null +++ b/lib/rules/at-rule-name-space-after/index.js @@ -0,0 +1,36 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const atRuleNameSpaceChecker = require("../atRuleNameSpaceChecker") + +const ruleName = "at-rule-name-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: name => `Expected single space after at-rule name \"${name}\"`, +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "always-single-line" ], + }) + if (!validOptions) { + return + } + + atRuleNameSpaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/at-rule-no-unknown/README.md b/lib/rules/at-rule-no-unknown/README.md similarity index 100% rename from src/rules/at-rule-no-unknown/README.md rename to lib/rules/at-rule-no-unknown/README.md diff --git a/src/rules/at-rule-no-unknown/__tests__/index.js b/lib/rules/at-rule-no-unknown/__tests__/index.js similarity index 93% rename from src/rules/at-rule-no-unknown/__tests__/index.js rename to lib/rules/at-rule-no-unknown/__tests__/index.js index 5f70952112..1363553128 100644 --- a/src/rules/at-rule-no-unknown/__tests__/index.js +++ b/lib/rules/at-rule-no-unknown/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/at-rule-no-unknown/index.js b/lib/rules/at-rule-no-unknown/index.js new file mode 100644 index 0000000000..7b51205934 --- /dev/null +++ b/lib/rules/at-rule-no-unknown/index.js @@ -0,0 +1,56 @@ +"use strict" + +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "at-rule-no-unknown" + +const messages = ruleMessages(ruleName, { + rejected: atRule => `Unexpected unknown at-rule "${atRule}"`, +}) + +const rule = function (actual, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }, { + actual: options, + possible: { + ignoreAtRules: [_.isString], + }, + optional: true, + }) + + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + const name = atRule.name + + // Return early if at-rule is to be ignored + + if (optionsMatches(options, "ignoreAtRules", atRule.name)) { + return + } + + if (postcss.vendor.prefix(name) || keywordSets.atRules.has(name.toLowerCase())) { + return + } + + report({ + message: messages.rejected(`@${name}`), + node: atRule, + ruleName, + result, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/at-rule-no-vendor-prefix/README.md b/lib/rules/at-rule-no-vendor-prefix/README.md similarity index 100% rename from src/rules/at-rule-no-vendor-prefix/README.md rename to lib/rules/at-rule-no-vendor-prefix/README.md diff --git a/src/rules/at-rule-no-vendor-prefix/__tests__/index.js b/lib/rules/at-rule-no-vendor-prefix/__tests__/index.js similarity index 80% rename from src/rules/at-rule-no-vendor-prefix/__tests__/index.js rename to lib/rules/at-rule-no-vendor-prefix/__tests__/index.js index 39c50eea1e..475d8957c9 100644 --- a/src/rules/at-rule-no-vendor-prefix/__tests__/index.js +++ b/lib/rules/at-rule-no-vendor-prefix/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/at-rule-no-vendor-prefix/index.js b/lib/rules/at-rule-no-vendor-prefix/index.js new file mode 100644 index 0000000000..af3cc1cce1 --- /dev/null +++ b/lib/rules/at-rule-no-vendor-prefix/index.js @@ -0,0 +1,49 @@ +"use strict" + +const isAutoprefixable = require("../../utils/isAutoprefixable") +const isStandardSyntaxAtRule = require("../../utils/isStandardSyntaxAtRule") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "at-rule-no-vendor-prefix" + +const messages = ruleMessages(ruleName, { + rejected: p => `Unexpected vendor-prefixed at-rule "@${p}"`, +}) + +const rule = function (actual) { + return function (root, result) { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + if (!isStandardSyntaxAtRule(atRule)) { + return + } + + const name = atRule.name + + if (name[0] !== "-") { + return + } + + if (!isAutoprefixable.atRuleName(name)) { + return + } + + report({ + message: messages.rejected(name), + node: atRule, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/at-rule-semicolon-newline-after/README.md b/lib/rules/at-rule-semicolon-newline-after/README.md similarity index 100% rename from src/rules/at-rule-semicolon-newline-after/README.md rename to lib/rules/at-rule-semicolon-newline-after/README.md diff --git a/src/rules/at-rule-semicolon-newline-after/__tests__/index.js b/lib/rules/at-rule-semicolon-newline-after/__tests__/index.js similarity index 91% rename from src/rules/at-rule-semicolon-newline-after/__tests__/index.js rename to lib/rules/at-rule-semicolon-newline-after/__tests__/index.js index bd1684580f..bf489de57a 100644 --- a/src/rules/at-rule-semicolon-newline-after/__tests__/index.js +++ b/lib/rules/at-rule-semicolon-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/at-rule-semicolon-newline-after/index.js b/lib/rules/at-rule-semicolon-newline-after/index.js new file mode 100644 index 0000000000..5824eabe8a --- /dev/null +++ b/lib/rules/at-rule-semicolon-newline-after/index.js @@ -0,0 +1,63 @@ +"use strict" + +const hasBlock = require("../../utils/hasBlock") +const nextNonCommentNode = require("../../utils/nextNonCommentNode") +const rawNodeString = require("../../utils/rawNodeString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") + +const ruleName = "at-rule-semicolon-newline-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected newline after \";\"", +}) + +const rule = function (actual) { + const checker = whitespaceChecker("newline", actual, messages) + + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual, + possible: ["always"], + }) + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + const nextNode = atRule.next() + if (!nextNode) { + return + } + if (hasBlock(atRule)) { + return + } + + // Allow an end-of-line comment + const nodeToCheck = nextNonCommentNode(nextNode) + if (!nodeToCheck) { + return + } + + checker.afterOneOnly({ + source: rawNodeString(nodeToCheck), + index: -1, + err: msg => { + report({ + message: msg, + node: atRule, + index: atRule.toString().length + 1, + result, + ruleName, + }) + }, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/at-rule-whitelist/README.md b/lib/rules/at-rule-whitelist/README.md similarity index 100% rename from src/rules/at-rule-whitelist/README.md rename to lib/rules/at-rule-whitelist/README.md diff --git a/src/rules/at-rule-whitelist/__tests__/index.js b/lib/rules/at-rule-whitelist/__tests__/index.js similarity index 92% rename from src/rules/at-rule-whitelist/__tests__/index.js rename to lib/rules/at-rule-whitelist/__tests__/index.js index 045a0d6e45..e4e38da5a5 100644 --- a/src/rules/at-rule-whitelist/__tests__/index.js +++ b/lib/rules/at-rule-whitelist/__tests__/index.js @@ -1,20 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "extend", - "import", - "keyframes", - ]], + config: [[ "extend", "import", "keyframes" ]], accept: [ { code: "a { color: pink; }", diff --git a/lib/rules/at-rule-whitelist/index.js b/lib/rules/at-rule-whitelist/index.js new file mode 100644 index 0000000000..e299cd06fb --- /dev/null +++ b/lib/rules/at-rule-whitelist/index.js @@ -0,0 +1,48 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "at-rule-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: name => `Unexpected at-rule "${name}"`, +}) + +const rule = function (whitelistInput) { + // To allow for just a string as a parameter (not only arrays of strings) + const whitelist = [].concat(whitelistInput) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + const name = atRule.name + + if (whitelist.indexOf(postcss.vendor.unprefixed(name).toLowerCase()) !== -1) { + return + } + + report({ + message: messages.rejected(name), + node: atRule, + result, + ruleName, + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/lib/rules/atRuleNameSpaceChecker.js b/lib/rules/atRuleNameSpaceChecker.js new file mode 100644 index 0000000000..2913356182 --- /dev/null +++ b/lib/rules/atRuleNameSpaceChecker.js @@ -0,0 +1,29 @@ +"use strict" + +const isStandardSyntaxAtRule = require("../utils/isStandardSyntaxAtRule") +const report = require("../utils/report") + +module.exports = function (options) { + options.root.walkAtRules(atRule => { + if (!isStandardSyntaxAtRule(atRule)) { + return + } + + checkColon(`@${atRule.name}${atRule.raws.afterName}${atRule.params}`, atRule.name.length, atRule) + }) + + function checkColon(source, index, node) { + options.locationChecker({ + source, + index, + err: m => report({ + message: m, + node, + index, + result: options.result, + ruleName: options.checkedRuleName, + }), + errTarget: `@${node.name}`, + }) + } +} diff --git a/src/rules/block-closing-brace-empty-line-before/README.md b/lib/rules/block-closing-brace-empty-line-before/README.md similarity index 100% rename from src/rules/block-closing-brace-empty-line-before/README.md rename to lib/rules/block-closing-brace-empty-line-before/README.md diff --git a/src/rules/block-closing-brace-empty-line-before/__tests__/index.js b/lib/rules/block-closing-brace-empty-line-before/__tests__/index.js similarity index 94% rename from src/rules/block-closing-brace-empty-line-before/__tests__/index.js rename to lib/rules/block-closing-brace-empty-line-before/__tests__/index.js index 8294323474..07cd44c8ae 100644 --- a/src/rules/block-closing-brace-empty-line-before/__tests__/index.js +++ b/lib/rules/block-closing-brace-empty-line-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/block-closing-brace-empty-line-before/index.js b/lib/rules/block-closing-brace-empty-line-before/index.js new file mode 100644 index 0000000000..fdb746d63a --- /dev/null +++ b/lib/rules/block-closing-brace-empty-line-before/index.js @@ -0,0 +1,77 @@ +"use strict" + +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const hasEmptyLine = require("../../utils/hasEmptyLine") +const isSingleLineString = require("../../utils/isSingleLineString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "block-closing-brace-empty-line-before" + +const messages = ruleMessages(ruleName, { + expected: "Expected empty line before closing brace", + rejected: "Unexpected empty line before closing brace", +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always-multi-line", "never" ], + }) + if (!validOptions) { + return + } + + // Check both kinds of statements: rules and at-rules + root.walkRules(check) + root.walkAtRules(check) + + function check(statement) { + // Return early if blockless or has empty block + if (!hasBlock(statement) || hasEmptyBlock(statement)) { + return + } + + // Get whitespace after ""}", ignoring extra semicolon + const before = (statement.raws.after || "").replace(/;+/, "") + if (before === undefined) { + return + } + + // Calculate index + const statementString = statement.toString() + let index = statementString.length - 1 + if (statementString[index - 1] === "\r") { + index -= 1 + } + + // Set expectation + const expectEmptyLineBefore = expectation === "always-multi-line" && !isSingleLineString(blockString(statement)) ? true : false + + // Check for at least one empty line + const hasEmptyLineBefore = hasEmptyLine(before) + + // Return if the expectation is met + if (expectEmptyLineBefore === hasEmptyLineBefore) { + return + } + + const message = expectEmptyLineBefore ? messages.expected : messages.rejected + report({ + message, + result, + ruleName, + node: statement, + index, + }) + } + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-closing-brace-newline-after/README.md b/lib/rules/block-closing-brace-newline-after/README.md similarity index 100% rename from src/rules/block-closing-brace-newline-after/README.md rename to lib/rules/block-closing-brace-newline-after/README.md diff --git a/src/rules/block-closing-brace-newline-after/__tests__/index.js b/lib/rules/block-closing-brace-newline-after/__tests__/index.js similarity index 98% rename from src/rules/block-closing-brace-newline-after/__tests__/index.js rename to lib/rules/block-closing-brace-newline-after/__tests__/index.js index a2487c526a..de23e518ea 100644 --- a/src/rules/block-closing-brace-newline-after/__tests__/index.js +++ b/lib/rules/block-closing-brace-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/block-closing-brace-newline-after/index.js b/lib/rules/block-closing-brace-newline-after/index.js similarity index 56% rename from src/rules/block-closing-brace-newline-after/index.js rename to lib/rules/block-closing-brace-newline-after/index.js index 4ffc65dfdc..a2b89c95a9 100644 --- a/src/rules/block-closing-brace-newline-after/index.js +++ b/lib/rules/block-closing-brace-newline-after/index.js @@ -1,18 +1,18 @@ -import { - blockString, - hasBlock, - optionsMatches, - rawNodeString, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { isString } from "lodash" +"use strict" -export const ruleName = "block-closing-brace-newline-after" +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const optionsMatches = require("../../utils/optionsMatches") +const rawNodeString = require("../../utils/rawNodeString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const _ = require("lodash") -export const messages = ruleMessages(ruleName, { +const ruleName = "block-closing-brace-newline-after" + +const messages = ruleMessages(ruleName, { expectedAfter: () => "Expected newline after \"}\"", expectedAfterSingleLine: () => "Expected newline after \"}\" of a single-line block", rejectedAfterSingleLine: () => "Unexpected whitespace after \"}\" of a single-line block", @@ -20,47 +20,47 @@ export const messages = ruleMessages(ruleName, { rejectedAfterMultiLine: () => "Unexpected whitespace after \"}\" of a multi-line block", }) -export default function (expectation, options) { +const rule = function (expectation, options) { const checker = whitespaceChecker("newline", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-single-line", - "never-single-line", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "always-single-line", "never-single-line", "always-multi-line", "never-multi-line" ], }, { actual: options, possible: { - ignoreAtRules: [isString], + ignoreAtRules: [_.isString], }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Check both kinds of statements: rules and at-rules root.walkRules(check) root.walkAtRules(check) function check(statement) { - if (!hasBlock(statement)) { return } - if (optionsMatches(options, "ignoreAtRules", statement.name)) { return } + if (!hasBlock(statement)) { + return + } + if (optionsMatches(options, "ignoreAtRules", statement.name)) { + return + } const nextNode = statement.next() - if (!nextNode) { return } + if (!nextNode) { + return + } // Allow an end-of-line comment x spaces after the brace - const nextNodeIsSingleLineComment = ( - nextNode.type === "comment" - && !/[^ ]/.test(nextNode.raws.before) - && nextNode.toString().indexOf("\n") === -1 - ) + const nextNodeIsSingleLineComment = nextNode.type === "comment" && !/[^ ]/.test(nextNode.raws.before) && nextNode.toString().indexOf("\n") === -1 - const nodeToCheck = (nextNodeIsSingleLineComment) ? nextNode.next() : nextNode - if (!nodeToCheck) { return } + const nodeToCheck = nextNodeIsSingleLineComment ? nextNode.next() : nextNode + if (!nodeToCheck) { + return + } let reportIndex = statement.toString().length let source = rawNodeString(nodeToCheck) @@ -90,3 +90,7 @@ export default function (expectation, options) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-closing-brace-newline-before/README.md b/lib/rules/block-closing-brace-newline-before/README.md similarity index 100% rename from src/rules/block-closing-brace-newline-before/README.md rename to lib/rules/block-closing-brace-newline-before/README.md diff --git a/src/rules/block-closing-brace-newline-before/__tests__/index.js b/lib/rules/block-closing-brace-newline-before/__tests__/index.js similarity index 97% rename from src/rules/block-closing-brace-newline-before/__tests__/index.js rename to lib/rules/block-closing-brace-newline-before/__tests__/index.js index 5f31f43360..7d6ef20dd6 100644 --- a/src/rules/block-closing-brace-newline-before/__tests__/index.js +++ b/lib/rules/block-closing-brace-newline-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/block-closing-brace-newline-before/index.js b/lib/rules/block-closing-brace-newline-before/index.js similarity index 61% rename from src/rules/block-closing-brace-newline-before/index.js rename to lib/rules/block-closing-brace-newline-before/index.js index 12259c70aa..3ff37c1f90 100644 --- a/src/rules/block-closing-brace-newline-before/index.js +++ b/lib/rules/block-closing-brace-newline-before/index.js @@ -1,33 +1,31 @@ -import { - blockString, - hasBlock, - hasEmptyBlock, - isSingleLineString, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { startsWith } from "lodash" +"use strict" -export const ruleName = "block-closing-brace-newline-before" +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const isSingleLineString = require("../../utils/isSingleLineString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") -export const messages = ruleMessages(ruleName, { +const ruleName = "block-closing-brace-newline-before" + +const messages = ruleMessages(ruleName, { expectedBefore: "Expected newline before \"}\"", expectedBeforeMultiLine: "Expected newline before \"}\" of a multi-line block", rejectedBeforeMultiLine: "Unexpected whitespace before \"}\" of a multi-line block", }) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Check both kinds of statements: rules and at-rules root.walkRules(check) @@ -35,24 +33,30 @@ export default function (expectation) { function check(statement) { // Return early if blockless or has empty block - if (!hasBlock(statement) || hasEmptyBlock(statement)) { return } + if (!hasBlock(statement) || hasEmptyBlock(statement)) { + return + } // Ignore extra semicolon const after = (statement.raws.after || "").replace(/;+/, "") - if (after === undefined) { return } + if (after === undefined) { + return + } const blockIsMultiLine = !isSingleLineString(blockString(statement)) const statementString = statement.toString() let index = statementString.length - 2 - if (statementString[index - 1] === "\r") { index -= 1 } + if (statementString[index - 1] === "\r") { + index -= 1 + } // We're really just checking whether a // newline *starts* the block's final space -- between // the last declaration and the closing brace. We can // ignore any other whitespace between them, because that // will be checked by the indentation rule. - if (!startsWith(after, "\n") && !startsWith(after, "\r\n")) { + if (!_.startsWith(after, "\n") && !_.startsWith(after, "\r\n")) { if (expectation === "always") { complain(messages.expectedBefore) } else if (blockIsMultiLine && expectation === "always-multi-line") { @@ -75,3 +79,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-closing-brace-space-after/README.md b/lib/rules/block-closing-brace-space-after/README.md similarity index 100% rename from src/rules/block-closing-brace-space-after/README.md rename to lib/rules/block-closing-brace-space-after/README.md diff --git a/src/rules/block-closing-brace-space-after/__tests__/index.js b/lib/rules/block-closing-brace-space-after/__tests__/index.js similarity index 98% rename from src/rules/block-closing-brace-space-after/__tests__/index.js rename to lib/rules/block-closing-brace-space-after/__tests__/index.js index 81bc6d06c0..a2ee89e3bc 100644 --- a/src/rules/block-closing-brace-space-after/__tests__/index.js +++ b/lib/rules/block-closing-brace-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/block-closing-brace-space-after/index.js b/lib/rules/block-closing-brace-space-after/index.js similarity index 62% rename from src/rules/block-closing-brace-space-after/index.js rename to lib/rules/block-closing-brace-space-after/index.js index c045695da9..526d1a9518 100644 --- a/src/rules/block-closing-brace-space-after/index.js +++ b/lib/rules/block-closing-brace-space-after/index.js @@ -1,16 +1,16 @@ -import { - blockString, - hasBlock, - rawNodeString, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" +"use strict" -export const ruleName = "block-closing-brace-space-after" +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const rawNodeString = require("../../utils/rawNodeString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "block-closing-brace-space-after" + +const messages = ruleMessages(ruleName, { expectedAfter: () => "Expected single space after \"}\"", rejectedAfter: () => "Unexpected whitespace after \"}\"", expectedAfterSingleLine: () => "Expected single space after \"}\" of a single-line block", @@ -19,22 +19,17 @@ export const messages = ruleMessages(ruleName, { rejectedAfterMultiLine: () => "Unexpected whitespace after \"}\" of a multi-line block", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return function (root, result) { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Check both kinds of statements: rules and at-rules root.walkRules(check) @@ -42,8 +37,12 @@ export default function (expectation) { function check(statement) { const nextNode = statement.next() - if (!nextNode) { return } - if (!hasBlock(statement)) { return } + if (!nextNode) { + return + } + if (!hasBlock(statement)) { + return + } let reportIndex = statement.toString().length let source = rawNodeString(nextNode) @@ -71,3 +70,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-closing-brace-space-before/README.md b/lib/rules/block-closing-brace-space-before/README.md similarity index 100% rename from src/rules/block-closing-brace-space-before/README.md rename to lib/rules/block-closing-brace-space-before/README.md diff --git a/src/rules/block-closing-brace-space-before/__tests__/index.js b/lib/rules/block-closing-brace-space-before/__tests__/index.js similarity index 97% rename from src/rules/block-closing-brace-space-before/__tests__/index.js rename to lib/rules/block-closing-brace-space-before/__tests__/index.js index 8ef8f0c1a3..6a5e48d062 100644 --- a/src/rules/block-closing-brace-space-before/__tests__/index.js +++ b/lib/rules/block-closing-brace-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/block-closing-brace-space-before/index.js b/lib/rules/block-closing-brace-space-before/index.js similarity index 58% rename from src/rules/block-closing-brace-space-before/index.js rename to lib/rules/block-closing-brace-space-before/index.js index e4c213638c..6216ddb007 100644 --- a/src/rules/block-closing-brace-space-before/index.js +++ b/lib/rules/block-closing-brace-space-before/index.js @@ -1,16 +1,16 @@ -import { - blockString, - hasBlock, - hasEmptyBlock, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" +"use strict" -export const ruleName = "block-closing-brace-space-before" +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "block-closing-brace-space-before" + +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected single space before \"}\"", rejectedBefore: () => "Unexpected whitespace before \"}\"", expectedBeforeSingleLine: () => "Expected single space before \"}\" of a single-line block", @@ -19,22 +19,17 @@ export const messages = ruleMessages(ruleName, { rejectedBeforeMultiLine: () => "Unexpected whitespace before \"}\" of a multi-line block", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Check both kinds of statement: rules and at-rules root.walkRules(check) @@ -42,13 +37,17 @@ export default function (expectation) { function check(statement) { // Return early if blockless or has empty block - if (!hasBlock(statement) || hasEmptyBlock(statement)) { return } + if (!hasBlock(statement) || hasEmptyBlock(statement)) { + return + } const source = blockString(statement) const statementString = statement.toString() let index = statementString.length - 2 - if (statementString[index - 1] === "\r") { index -= 1 } + if (statementString[index - 1] === "\r") { + index -= 1 + } checker.before({ source, @@ -66,3 +65,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-no-empty/README.md b/lib/rules/block-no-empty/README.md similarity index 100% rename from src/rules/block-no-empty/README.md rename to lib/rules/block-no-empty/README.md diff --git a/src/rules/block-no-empty/__tests__/index.js b/lib/rules/block-no-empty/__tests__/index.js similarity index 90% rename from src/rules/block-no-empty/__tests__/index.js rename to lib/rules/block-no-empty/__tests__/index.js index c12af138b0..6f0bbf0392 100644 --- a/src/rules/block-no-empty/__tests__/index.js +++ b/lib/rules/block-no-empty/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/block-no-empty/index.js b/lib/rules/block-no-empty/index.js similarity index 51% rename from src/rules/block-no-empty/index.js rename to lib/rules/block-no-empty/index.js index 2bef94eb77..e19d31a3ea 100644 --- a/src/rules/block-no-empty/index.js +++ b/lib/rules/block-no-empty/index.js @@ -1,28 +1,32 @@ -import { - beforeBlockString, - hasEmptyBlock, - report, - ruleMessages, - validateOptions, -} from "../../utils" +"use strict" -export const ruleName = "block-no-empty" +const beforeBlockString = require("../../utils/beforeBlockString") +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") -export const messages = ruleMessages(ruleName, { +const ruleName = "block-no-empty" + +const messages = ruleMessages(ruleName, { rejected: "Unexpected empty block", }) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Check both kinds of statements: rules and at-rules root.walkRules(check) root.walkAtRules(check) function check(statement) { - if (!hasEmptyBlock(statement)) { return } + if (!hasEmptyBlock(statement)) { + return + } let index = beforeBlockString(statement, { noRawBefore: true }).length @@ -41,3 +45,7 @@ export default function (actual) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-no-single-line/README.md b/lib/rules/block-no-single-line/README.md similarity index 100% rename from src/rules/block-no-single-line/README.md rename to lib/rules/block-no-single-line/README.md diff --git a/src/rules/block-no-single-line/__tests__/index.js b/lib/rules/block-no-single-line/__tests__/index.js similarity index 92% rename from src/rules/block-no-single-line/__tests__/index.js rename to lib/rules/block-no-single-line/__tests__/index.js index 7861fa10ad..5536329746 100644 --- a/src/rules/block-no-single-line/__tests__/index.js +++ b/lib/rules/block-no-single-line/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/block-no-single-line/index.js b/lib/rules/block-no-single-line/index.js new file mode 100644 index 0000000000..eeceb4a318 --- /dev/null +++ b/lib/rules/block-no-single-line/index.js @@ -0,0 +1,50 @@ +"use strict" + +const beforeBlockString = require("../../utils/beforeBlockString") +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const isSingleLineString = require("../../utils/isSingleLineString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "block-no-single-line" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected single-line block", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + // Check both kinds of statements: rules and at-rules + root.walkRules(check) + root.walkAtRules(check) + + function check(statement) { + if (!hasBlock(statement) || hasEmptyBlock(statement)) { + return + } + if (!isSingleLineString(blockString(statement))) { + return + } + + report({ + message: messages.rejected, + node: statement, + index: beforeBlockString(statement, { noRawBefore: true }).length, + result, + ruleName, + }) + } + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-opening-brace-newline-after/README.md b/lib/rules/block-opening-brace-newline-after/README.md similarity index 100% rename from src/rules/block-opening-brace-newline-after/README.md rename to lib/rules/block-opening-brace-newline-after/README.md diff --git a/src/rules/block-opening-brace-newline-after/__tests__/index.js b/lib/rules/block-opening-brace-newline-after/__tests__/index.js similarity index 96% rename from src/rules/block-opening-brace-newline-after/__tests__/index.js rename to lib/rules/block-opening-brace-newline-after/__tests__/index.js index 063835d973..f3cf391891 100644 --- a/src/rules/block-opening-brace-newline-after/__tests__/index.js +++ b/lib/rules/block-opening-brace-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/block-opening-brace-newline-after/index.js b/lib/rules/block-opening-brace-newline-after/index.js similarity index 51% rename from src/rules/block-opening-brace-newline-after/index.js rename to lib/rules/block-opening-brace-newline-after/index.js index c435f9c726..6661450702 100644 --- a/src/rules/block-opening-brace-newline-after/index.js +++ b/lib/rules/block-opening-brace-newline-after/index.js @@ -1,37 +1,35 @@ -import { - beforeBlockString, - blockString, - hasBlock, - hasEmptyBlock, - nextNonCommentNode, - rawNodeString, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" +"use strict" -export const ruleName = "block-opening-brace-newline-after" +const beforeBlockString = require("../../utils/beforeBlockString") +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const nextNonCommentNode = require("../../utils/nextNonCommentNode") +const rawNodeString = require("../../utils/rawNodeString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "block-opening-brace-newline-after" + +const messages = ruleMessages(ruleName, { expectedAfter: () => "Expected newline after \"{\"", expectedAfterMultiLine: () => "Expected newline after \"{\" of a multi-line block", rejectedAfterMultiLine: () => "Unexpected whitespace after \"{\" of a multi-line block", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("newline", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Check both kinds of statement: rules and at-rules root.walkRules(check) @@ -39,11 +37,15 @@ export default function (expectation) { function check(statement) { // Return early if blockless or has an empty block - if (!hasBlock(statement) || hasEmptyBlock(statement)) { return } + if (!hasBlock(statement) || hasEmptyBlock(statement)) { + return + } // Allow an end-of-line comment const nodeToCheck = nextNonCommentNode(statement.first) - if (!nodeToCheck) { return } + if (!nodeToCheck) { + return + } checker.afterOneOnly({ source: rawNodeString(nodeToCheck), @@ -62,3 +64,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-opening-brace-newline-before/README.md b/lib/rules/block-opening-brace-newline-before/README.md similarity index 100% rename from src/rules/block-opening-brace-newline-before/README.md rename to lib/rules/block-opening-brace-newline-before/README.md diff --git a/src/rules/block-opening-brace-newline-before/__tests__/index.js b/lib/rules/block-opening-brace-newline-before/__tests__/index.js similarity index 98% rename from src/rules/block-opening-brace-newline-before/__tests__/index.js rename to lib/rules/block-opening-brace-newline-before/__tests__/index.js index dbf4fc95b5..290475f8fc 100644 --- a/src/rules/block-opening-brace-newline-before/__tests__/index.js +++ b/lib/rules/block-opening-brace-newline-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/block-opening-brace-newline-before/index.js b/lib/rules/block-opening-brace-newline-before/index.js similarity index 57% rename from src/rules/block-opening-brace-newline-before/index.js rename to lib/rules/block-opening-brace-newline-before/index.js index bbbbb0ccd6..f754df81b2 100644 --- a/src/rules/block-opening-brace-newline-before/index.js +++ b/lib/rules/block-opening-brace-newline-before/index.js @@ -1,17 +1,17 @@ -import { - beforeBlockString, - blockString, - hasBlock, - hasEmptyBlock, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" +"use strict" -export const ruleName = "block-opening-brace-newline-before" +const beforeBlockString = require("../../utils/beforeBlockString") +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "block-opening-brace-newline-before" + +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected newline before \"{\"", expectedBeforeSingleLine: () => "Expected newline before \"{\" of a single-line block", rejectedBeforeSingleLine: () => "Unexpected whitespace before \"{\" of a single-line block", @@ -19,21 +19,17 @@ export const messages = ruleMessages(ruleName, { rejectedBeforeMultiLine: () => "Unexpected whitespace before \"{\" of a multi-line block", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("newline", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-single-line", - "never-single-line", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "always-single-line", "never-single-line", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Check both kinds of statement: rules and at-rules root.walkRules(check) @@ -41,13 +37,17 @@ export default function (expectation) { function check(statement) { // Return early if blockless or has an empty block - if (!hasBlock(statement) || hasEmptyBlock(statement)) { return } + if (!hasBlock(statement) || hasEmptyBlock(statement)) { + return + } const source = beforeBlockString(statement) const beforeBraceNoRaw = beforeBlockString(statement, { noRawBefore: true }) let index = beforeBraceNoRaw.length - 1 - if (beforeBraceNoRaw[index - 1] === "\r") { index -= 1 } + if (beforeBraceNoRaw[index - 1] === "\r") { + index -= 1 + } checker.beforeAllowingIndentation({ lineCheckStr: blockString(statement), @@ -66,3 +66,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-opening-brace-space-after/README.md b/lib/rules/block-opening-brace-space-after/README.md similarity index 100% rename from src/rules/block-opening-brace-space-after/README.md rename to lib/rules/block-opening-brace-space-after/README.md diff --git a/src/rules/block-opening-brace-space-after/__tests__/index.js b/lib/rules/block-opening-brace-space-after/__tests__/index.js similarity index 97% rename from src/rules/block-opening-brace-space-after/__tests__/index.js rename to lib/rules/block-opening-brace-space-after/__tests__/index.js index f2871bc595..4d4da97d76 100644 --- a/src/rules/block-opening-brace-space-after/__tests__/index.js +++ b/lib/rules/block-opening-brace-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/block-opening-brace-space-after/index.js b/lib/rules/block-opening-brace-space-after/index.js similarity index 56% rename from src/rules/block-opening-brace-space-after/index.js rename to lib/rules/block-opening-brace-space-after/index.js index 128af6e526..c73848fdff 100644 --- a/src/rules/block-opening-brace-space-after/index.js +++ b/lib/rules/block-opening-brace-space-after/index.js @@ -1,17 +1,17 @@ -import { - beforeBlockString, - blockString, - hasBlock, - hasEmptyBlock, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -export const ruleName = "block-opening-brace-space-after" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const beforeBlockString = require("../../utils/beforeBlockString") +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") + +const ruleName = "block-opening-brace-space-after" + +const messages = ruleMessages(ruleName, { expectedAfter: () => "Expected single space after \"{\"", rejectedAfter: () => "Unexpected whitespace after \"{\"", expectedAfterSingleLine: () => "Expected single space after \"{\" of a single-line block", @@ -20,21 +20,16 @@ export const messages = ruleMessages(ruleName, { rejectedAfterMultiLine: () => "Unexpected whitespace after \"{\" of a multi-line block", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Check both kinds of statements: rules and at-rules root.walkRules(check) @@ -42,7 +37,9 @@ export default function (expectation) { function check(statement) { // Return early if blockless or has an empty block - if (!hasBlock(statement) || hasEmptyBlock(statement)) { return } + if (!hasBlock(statement) || hasEmptyBlock(statement)) { + return + } checker.after({ source: blockString(statement), @@ -60,3 +57,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/block-opening-brace-space-before/README.md b/lib/rules/block-opening-brace-space-before/README.md similarity index 100% rename from src/rules/block-opening-brace-space-before/README.md rename to lib/rules/block-opening-brace-space-before/README.md diff --git a/src/rules/block-opening-brace-space-before/__tests__/index.js b/lib/rules/block-opening-brace-space-before/__tests__/index.js similarity index 97% rename from src/rules/block-opening-brace-space-before/__tests__/index.js rename to lib/rules/block-opening-brace-space-before/__tests__/index.js index 5721dd1bec..e3f63494d1 100644 --- a/src/rules/block-opening-brace-space-before/__tests__/index.js +++ b/lib/rules/block-opening-brace-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/block-opening-brace-space-before/index.js b/lib/rules/block-opening-brace-space-before/index.js similarity index 57% rename from src/rules/block-opening-brace-space-before/index.js rename to lib/rules/block-opening-brace-space-before/index.js index 6e5ed58864..92d85f576e 100644 --- a/src/rules/block-opening-brace-space-before/index.js +++ b/lib/rules/block-opening-brace-space-before/index.js @@ -1,19 +1,19 @@ -import { - beforeBlockString, - blockString, - hasBlock, - hasEmptyBlock, - optionsMatches, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { isString } from "lodash" +"use strict" -export const ruleName = "block-opening-brace-space-before" +const beforeBlockString = require("../../utils/beforeBlockString") +const blockString = require("../../utils/blockString") +const hasBlock = require("../../utils/hasBlock") +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const _ = require("lodash") -export const messages = ruleMessages(ruleName, { +const ruleName = "block-opening-brace-space-before" + +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected single space before \"{\"", rejectedBefore: () => "Unexpected whitespace before \"{\"", expectedBeforeSingleLine: () => "Expected single space before \"{\" of a single-line block", @@ -22,27 +22,22 @@ export const messages = ruleMessages(ruleName, { rejectedBeforeMultiLine: () => "Unexpected whitespace before \"{\" of a multi-line block", }) -export default function (expectation, options) { +const rule = function (expectation, options) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line", "always-multi-line", "never-multi-line" ], }, { actual: options, possible: { - ignoreAtRules: [isString], + ignoreAtRules: [_.isString], }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Check both kinds of statements: rules and at-rules root.walkRules(check) @@ -50,16 +45,22 @@ export default function (expectation, options) { function check(statement) { // Return early if blockless or has an empty block - if (!hasBlock(statement) || hasEmptyBlock(statement)) { return } + if (!hasBlock(statement) || hasEmptyBlock(statement)) { + return + } // Return early if at-rule is to be ignored - if (optionsMatches(options, "ignoreAtRules", statement.name)) { return } + if (optionsMatches(options, "ignoreAtRules", statement.name)) { + return + } const source = beforeBlockString(statement) const beforeBraceNoRaw = beforeBlockString(statement, { noRawBefore: true }) let index = beforeBraceNoRaw.length - 1 - if (beforeBraceNoRaw[index - 1] === "\r") { index -= 1 } + if (beforeBraceNoRaw[index - 1] === "\r") { + index -= 1 + } checker.before({ source, @@ -78,3 +79,7 @@ export default function (expectation, options) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/lib/rules/checkRuleEmptyLineBefore.js b/lib/rules/checkRuleEmptyLineBefore.js new file mode 100644 index 0000000000..89e9845341 --- /dev/null +++ b/lib/rules/checkRuleEmptyLineBefore.js @@ -0,0 +1,46 @@ +"use strict" + +const hasEmptyLine = require("../utils/hasEmptyLine") +const isSingleLineString = require("../utils/isSingleLineString") +const optionsMatches = require("../utils/optionsMatches") +const report = require("../utils/report") + +module.exports = function (opts) { + let expectEmptyLineBefore = opts.expectation.indexOf("always") !== -1 ? true : false + + // Optionally ignore the expectation if a comment precedes this node + if (optionsMatches(opts.options, "ignore", "after-comment") && opts.rule.prev() && opts.rule.prev().type === "comment") { + return + } + + // Ignore if the expectation is for multiple and the rule is single-line + if (opts.expectation.indexOf("multi-line") !== -1 && isSingleLineString(opts.rule.toString())) { + return + } + + // Optionally reverse the expectation for the first nested node + if (optionsMatches(opts.options, "except", "first-nested") && opts.rule === opts.rule.parent.first) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Optionally reverse the expectation for single line comments + if (optionsMatches(opts.options, "except", "after-single-line-comment") && opts.rule.prev() && opts.rule.prev().type === "comment" && isSingleLineString(opts.rule.prev().toString())) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + const hasEmptyLineBefore = hasEmptyLine(opts.rule.raws.before) + + // Return if the expectation is met + if (expectEmptyLineBefore === hasEmptyLineBefore) { + return + } + + const message = expectEmptyLineBefore ? opts.messages.expected : opts.messages.rejected + + report({ + message, + node: opts.rule, + result: opts.result, + ruleName: opts.checkedRuleName, + }) +} diff --git a/src/rules/color-hex-case/README.md b/lib/rules/color-hex-case/README.md similarity index 100% rename from src/rules/color-hex-case/README.md rename to lib/rules/color-hex-case/README.md diff --git a/src/rules/color-hex-case/__tests__/index.js b/lib/rules/color-hex-case/__tests__/index.js similarity index 90% rename from src/rules/color-hex-case/__tests__/index.js rename to lib/rules/color-hex-case/__tests__/index.js index 3ee73de1f2..38d4517938 100644 --- a/src/rules/color-hex-case/__tests__/index.js +++ b/lib/rules/color-hex-case/__tests__/index.js @@ -1,12 +1,10 @@ -import { - mergeTestDescriptions, - testRule, -} from "../../../testUtils" -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" +"use strict" + +const mergeTestDescriptions = require("../../../testUtils/mergeTestDescriptions") +const testRule = require("../../../testUtils/testRule") +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") const rule = rules[ruleName] diff --git a/src/rules/color-hex-case/index.js b/lib/rules/color-hex-case/index.js similarity index 58% rename from src/rules/color-hex-case/index.js rename to lib/rules/color-hex-case/index.js index bbe9b231a7..6374f39176 100644 --- a/src/rules/color-hex-case/index.js +++ b/lib/rules/color-hex-case/index.js @@ -1,39 +1,42 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" +"use strict" -export const ruleName = "color-hex-case" +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "color-hex-case" + +const messages = ruleMessages(ruleName, { expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, }) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "lower", - "upper", - ], + possible: [ "lower", "upper" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { const declString = decl.toString() styleSearch({ source: declString, target: "#" }, match => { const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex)) - if (!hexMatch) { return } + if (!hexMatch) { + return + } const hexValue = hexMatch[0] const hexValueLower = hexValue.toLowerCase() const hexValueUpper = hexValue.toUpperCase() const expectedHex = expectation === "lower" ? hexValueLower : hexValueUpper - if (hexValue === expectedHex) { return } + if (hexValue === expectedHex) { + return + } report({ message: messages.expected(hexValue, expectedHex), @@ -46,3 +49,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/color-hex-length/README.md b/lib/rules/color-hex-length/README.md similarity index 100% rename from src/rules/color-hex-length/README.md rename to lib/rules/color-hex-length/README.md diff --git a/src/rules/color-hex-length/__tests__/index.js b/lib/rules/color-hex-length/__tests__/index.js similarity index 91% rename from src/rules/color-hex-length/__tests__/index.js rename to lib/rules/color-hex-length/__tests__/index.js index 7d5e60bde0..66acb27a6f 100644 --- a/src/rules/color-hex-length/__tests__/index.js +++ b/lib/rules/color-hex-length/__tests__/index.js @@ -1,12 +1,10 @@ -import { - mergeTestDescriptions, - testRule, -} from "../../../testUtils" -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" +"use strict" + +const mergeTestDescriptions = require("../../../testUtils/mergeTestDescriptions") +const testRule = require("../../../testUtils/testRule") +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") const rule = rules[ruleName] diff --git a/src/rules/color-hex-length/index.js b/lib/rules/color-hex-length/index.js similarity index 61% rename from src/rules/color-hex-length/index.js rename to lib/rules/color-hex-length/index.js index ffde265a7e..ea7b1e166c 100644 --- a/src/rules/color-hex-length/index.js +++ b/lib/rules/color-hex-length/index.js @@ -1,39 +1,44 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" +"use strict" -export const ruleName = "color-hex-length" +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "color-hex-length" + +const messages = ruleMessages(ruleName, { expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, }) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "short", - "long", - ], + possible: [ "short", "long" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { const declString = decl.toString() styleSearch({ source: declString, target: "#" }, match => { const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex)) - if (!hexMatch) { return } + if (!hexMatch) { + return + } const hexValue = hexMatch[0] - if (expectation === "long" && hexValue.length !== 4 && hexValue.length !== 5) { return } + if (expectation === "long" && hexValue.length !== 4 && hexValue.length !== 5) { + return + } - if (expectation === "short" && (hexValue.length < 6 || !canShrink(hexValue))) { return } + if (expectation === "short" && (hexValue.length < 6 || !canShrink(hexValue))) { + return + } const variant = expectation === "long" ? longer : shorter @@ -52,11 +57,7 @@ export default function (expectation) { function canShrink(hex) { hex = hex.toLowerCase() - return ( - hex[1] === hex[2] - && hex[3] === hex[4] - && hex[5] === hex[6] - && (hex.length === 7 || (hex.length === 9 && hex[7] === hex[8]))) + return hex[1] === hex[2] && hex[3] === hex[4] && hex[5] === hex[6] && (hex.length === 7 || hex.length === 9 && hex[7] === hex[8]) } function shorter(hex) { @@ -74,3 +75,7 @@ function longer(hex) { } return hexVariant } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/color-named/README.md b/lib/rules/color-named/README.md similarity index 100% rename from src/rules/color-named/README.md rename to lib/rules/color-named/README.md diff --git a/src/rules/color-named/__tests__/index.js b/lib/rules/color-named/__tests__/index.js similarity index 96% rename from src/rules/color-named/__tests__/index.js rename to lib/rules/color-named/__tests__/index.js index a8f81b25f5..4a169a50ca 100644 --- a/src/rules/color-named/__tests__/index.js +++ b/lib/rules/color-named/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -265,9 +265,9 @@ testRule(rule, { skipBasicChecks: true, accept: [ { - code: "a { composes: blue from 'src/index.css'; }", + code: "a { composes: blue from 'lib/index.css'; }", }, { - code: "a { composes: grey blue from 'src/index.css'; }", + code: "a { composes: grey blue from 'lib/index.css'; }", }, { code: "a { my-property: red; }", }, { diff --git a/lib/rules/color-named/index.js b/lib/rules/color-named/index.js new file mode 100644 index 0000000000..34e225a0f5 --- /dev/null +++ b/lib/rules/color-named/index.js @@ -0,0 +1,124 @@ +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const propertySets = require("../../reference/propertySets") +const keywordSets = require("../../reference/keywordSets") +const _ = require("lodash") +const namedColorData = require("../../reference/namedColorData") +const valueParser = require("postcss-value-parser") + +const ruleName = "color-named" + +const messages = ruleMessages(ruleName, { + expected: (named, original) => `Expected "${original}" to be "${named}"`, + rejected: named => `Unexpected named color "${named}"`, +}) + +// Todo tested on case insensivity +const NODE_TYPES = [ "word", "function" ] + +const rule = function (expectation, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "never", "always-where-possible" ], + }, { + actual: options, + possible: { + ignoreProperties: [_.isString], + ignore: ["inside-function"], + }, + optional: true, + }) + + if (!validOptions) { + return + } + + const namedColors = Object.keys(namedColorData) + + root.walkDecls(decl => { + if (propertySets.acceptCustomIdents.has(decl.prop)) { + return + } + + // Return early if the property is to be ignored + if (optionsMatches(options, "ignoreProperties", decl.prop)) { + return + } + + valueParser(decl.value).walk(node => { + const value = node.value, + type = node.type, + sourceIndex = node.sourceIndex + + if (optionsMatches(options, "ignore", "inside-function") && type === "function") { + return false + } + + if (!isStandardSyntaxValue(value)) { + return + } + // Return early if neither a word nor a function + if (NODE_TYPES.indexOf(type) === -1) { + return + } + + // Check for named colors for "never" option + if (expectation === "never" && type === "word" && namedColors.indexOf(value.toLowerCase()) !== -1) { + complain(messages.rejected(value), decl, declarationValueIndex(decl) + sourceIndex) + return + } + + // Check "always-where-possible" option ... + if (expectation !== "always-where-possible") { + return + } + + // First by checking for alternative color function representations ... + if (type === "function" && keywordSets.colorFunctionNames.has(value.toLowerCase())) { + // Remove all spaces to match what's in `representations` + const normalizedFunctionString = valueParser.stringify(node).replace(/\s+/g, "") + let namedColor + for (let i = 0, l = namedColors.length; i < l; i++) { + namedColor = namedColors[i] + if (namedColorData[namedColor].func.indexOf(normalizedFunctionString.toLowerCase()) !== -1) { + complain(messages.expected(namedColor, normalizedFunctionString), decl, declarationValueIndex(decl) + sourceIndex) + return // Exit as soon as a problem is found + } + } + return + } + + // Then by checking for alternative hex representations + let namedColor + for (let i = 0, l = namedColors.length; i < l; i++) { + namedColor = namedColors[i] + if (namedColorData[namedColor].hex.indexOf(value.toLowerCase()) !== -1) { + complain(messages.expected(namedColor, value), decl, declarationValueIndex(decl) + sourceIndex) + return // Exit as soon as a problem is found + } + } + }) + }) + + function complain(message, node, index) { + report({ + result, + ruleName, + message, + node, + index, + }) + } + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/color-no-hex/README.md b/lib/rules/color-no-hex/README.md similarity index 100% rename from src/rules/color-no-hex/README.md rename to lib/rules/color-no-hex/README.md diff --git a/src/rules/color-no-hex/__tests__/index.js b/lib/rules/color-no-hex/__tests__/index.js similarity index 74% rename from src/rules/color-no-hex/__tests__/index.js rename to lib/rules/color-no-hex/__tests__/index.js index 18fa27e8f5..a91b60f275 100644 --- a/src/rules/color-no-hex/__tests__/index.js +++ b/lib/rules/color-no-hex/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -28,14 +28,7 @@ testRule(rule, { code: "a { box-sizing: #$type-box; }", description: "ignore sass-like interpolation", }, { - code: "@font-face {\n" + - "font-family: dashicons;\n" + - "src: url(data:application/font-woff;charset=utf-8;base64, ABCDEF==) format(\"woff\"),\n" + - "url(../fonts/dashicons.ttf) format(\"truetype\"),\n" + - "url(../fonts/dashicons.svg#dashicons) format(\"svg\");\n" + - "font-weight: normal;\n" + - "font-style: normal;\n" + - "}", + code: "@font-face {\n" + "font-family: dashicons;\n" + "src: url(data:application/font-woff;charset=utf-8;base64, ABCDEF==) format(\"woff\"),\n" + "url(../fonts/dashicons.ttf) format(\"truetype\"),\n" + "url(../fonts/dashicons.svg#dashicons) format(\"svg\");\n" + "font-weight: normal;\n" + "font-style: normal;\n" + "}", } ], reject: [ { diff --git a/src/rules/color-no-hex/index.js b/lib/rules/color-no-hex/index.js similarity index 58% rename from src/rules/color-no-hex/index.js rename to lib/rules/color-no-hex/index.js index f5f89b8537..60881619a0 100644 --- a/src/rules/color-no-hex/index.js +++ b/lib/rules/color-no-hex/index.js @@ -1,20 +1,22 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" +"use strict" -export const ruleName = "color-no-hex" +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "color-no-hex" + +const messages = ruleMessages(ruleName, { rejected: hex => `Unexpected hex color "${hex}"`, }) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { const declString = decl.toString() @@ -23,10 +25,14 @@ export default function (actual) { // If there's not a colon, comma, or whitespace character before, we'll assume this is // not intended to be a hex color, but is instead something like the // hash in a url() argument - if (!/[:,\s]/.test(declString[match.startIndex - 1])) { return } + if (!/[:,\s]/.test(declString[match.startIndex - 1])) { + return + } const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex)) - if (!hexMatch) { return } + if (!hexMatch) { + return + } const hexValue = hexMatch[0] report({ @@ -40,3 +46,7 @@ export default function (actual) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/color-no-invalid-hex/README.md b/lib/rules/color-no-invalid-hex/README.md similarity index 100% rename from src/rules/color-no-invalid-hex/README.md rename to lib/rules/color-no-invalid-hex/README.md diff --git a/src/rules/color-no-invalid-hex/__tests__/index.js b/lib/rules/color-no-invalid-hex/__tests__/index.js similarity index 69% rename from src/rules/color-no-invalid-hex/__tests__/index.js rename to lib/rules/color-no-invalid-hex/__tests__/index.js index 40c04a01c3..15a24ca704 100644 --- a/src/rules/color-no-invalid-hex/__tests__/index.js +++ b/lib/rules/color-no-invalid-hex/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -34,14 +34,7 @@ testRule(rule, { code: "a { box-sizing: #$type-box; }", description: "ignore sass-like interpolation", }, { - code: "@font-face {\n" + - "font-family: dashicons;\n" + - "src: url(data:application/font-woff;charset=utf-8;base64, ABCDEF==) format(\"woff\"),\n" + - "url(../fonts/dashicons.ttf) format(\"truetype\"),\n" + - "url(../fonts/dashicons.svg#dashicons) format(\"svg\");\n" + - "font-weight: normal;\n" + - "font-style: normal;\n" + - "}", + code: "@font-face {\n" + "font-family: dashicons;\n" + "src: url(data:application/font-woff;charset=utf-8;base64, ABCDEF==) format(\"woff\"),\n" + "url(../fonts/dashicons.ttf) format(\"truetype\"),\n" + "url(../fonts/dashicons.svg#dashicons) format(\"svg\");\n" + "font-weight: normal;\n" + "font-style: normal;\n" + "}", } ], reject: [ { diff --git a/src/rules/color-no-invalid-hex/index.js b/lib/rules/color-no-invalid-hex/index.js similarity index 53% rename from src/rules/color-no-invalid-hex/index.js rename to lib/rules/color-no-invalid-hex/index.js index 263380f328..6b14a596bd 100644 --- a/src/rules/color-no-invalid-hex/index.js +++ b/lib/rules/color-no-invalid-hex/index.js @@ -1,21 +1,23 @@ -import { - isValidHex, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" +"use strict" -export const ruleName = "color-no-invalid-hex" +const isValidHex = require("../../utils/isValidHex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "color-no-invalid-hex" + +const messages = ruleMessages(ruleName, { rejected: hex => `Unexpected invalid hex color "${hex}"`, }) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { const declString = decl.toString() @@ -24,13 +26,19 @@ export default function (actual) { // If there's not a colon, comma, or whitespace character before, we'll assume this is // not intended to be a hex color, but is instead something like the // hash in a url() argument - if (!/[:,\s]/.test(declString[match.startIndex - 1])) { return } + if (!/[:,\s]/.test(declString[match.startIndex - 1])) { + return + } const hexMatch = /^#[0-9A-Za-z]+/.exec(declString.substr(match.startIndex)) - if (!hexMatch) { return } + if (!hexMatch) { + return + } const hexValue = hexMatch[0] - if (isValidHex(hexValue)) { return } + if (isValidHex(hexValue)) { + return + } report({ message: messages.rejected(hexValue), @@ -43,3 +51,7 @@ export default function (actual) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/comment-empty-line-before/README.md b/lib/rules/comment-empty-line-before/README.md similarity index 100% rename from src/rules/comment-empty-line-before/README.md rename to lib/rules/comment-empty-line-before/README.md diff --git a/src/rules/comment-empty-line-before/__tests__/index.js b/lib/rules/comment-empty-line-before/__tests__/index.js similarity index 94% rename from src/rules/comment-empty-line-before/__tests__/index.js rename to lib/rules/comment-empty-line-before/__tests__/index.js index 81b792dea2..de059a6b25 100644 --- a/src/rules/comment-empty-line-before/__tests__/index.js +++ b/lib/rules/comment-empty-line-before/__tests__/index.js @@ -1,12 +1,10 @@ -import { - mergeTestDescriptions, - testRule, -} from "../../../testUtils" -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" +"use strict" + +const mergeTestDescriptions = require("../../../testUtils/mergeTestDescriptions") +const testRule = require("../../../testUtils/testRule") +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") const rule = rules[ruleName] diff --git a/lib/rules/comment-empty-line-before/index.js b/lib/rules/comment-empty-line-before/index.js new file mode 100644 index 0000000000..7344500099 --- /dev/null +++ b/lib/rules/comment-empty-line-before/index.js @@ -0,0 +1,91 @@ +"use strict" + +const hasEmptyLine = require("../../utils/hasEmptyLine") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "comment-empty-line-before" + +const messages = ruleMessages(ruleName, { + expected: "Expected empty line before comment", + rejected: "Unexpected empty line before comment", +}) + +const stylelintCommandPrefix = "stylelint-" + +const rule = function (expectation, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }, { + actual: options, + possible: { + except: ["first-nested"], + ignore: [ "stylelint-commands", "between-comments" ], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkComments(comment => { + // Ignore the first node + if (comment === root.first) { + return + } + + // Optionally ignore stylelint commands + if (comment.text.indexOf(stylelintCommandPrefix) === 0 && optionsMatches(options, "ignore", "stylelint-commands")) { + return + } + + // Optionally ignore newlines between comments + const prev = comment.prev() + if (prev && prev.type === "comment" && optionsMatches(options, "ignore", "between-comments")) { + return + } + + if (comment.raws.inline || comment.inline) { + return + } + + const before = comment.raws.before + + // Ignore shared-line comments + if (before.indexOf("\n") === -1) { + return + } + + const expectEmptyLineBefore = (() => { + if (optionsMatches(options, "except", "first-nested") && comment.parent !== root && comment === comment.parent.first) { + return false + } + return expectation === "always" + })() + + const hasEmptyLineBefore = hasEmptyLine(before) + + // Return if the expectation is met + if (expectEmptyLineBefore === hasEmptyLineBefore) { + return + } + + const message = expectEmptyLineBefore ? messages.expected : messages.rejected + + report({ + message, + node: comment, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/comment-no-empty/README.md b/lib/rules/comment-no-empty/README.md similarity index 100% rename from src/rules/comment-no-empty/README.md rename to lib/rules/comment-no-empty/README.md diff --git a/src/rules/comment-no-empty/__tests__/index.js b/lib/rules/comment-no-empty/__tests__/index.js similarity index 84% rename from src/rules/comment-no-empty/__tests__/index.js rename to lib/rules/comment-no-empty/__tests__/index.js index 5134d7019b..df9765de45 100644 --- a/src/rules/comment-no-empty/__tests__/index.js +++ b/lib/rules/comment-no-empty/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/comment-no-empty/index.js b/lib/rules/comment-no-empty/index.js new file mode 100644 index 0000000000..6afe5f0e1a --- /dev/null +++ b/lib/rules/comment-no-empty/index.js @@ -0,0 +1,41 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "comment-no-empty" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected empty comment", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkComments(comment => { + // To ignore inline SCSS comments + if (comment.raws.inline || comment.inline) { + return + } + // To ignore comments that are not empty + if (comment.text && comment.text.length !== 0) { + return + } + report({ + message: messages.rejected, + node: comment, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/comment-whitespace-inside/README.md b/lib/rules/comment-whitespace-inside/README.md similarity index 100% rename from src/rules/comment-whitespace-inside/README.md rename to lib/rules/comment-whitespace-inside/README.md diff --git a/src/rules/comment-whitespace-inside/__tests__/index.js b/lib/rules/comment-whitespace-inside/__tests__/index.js similarity index 95% rename from src/rules/comment-whitespace-inside/__tests__/index.js rename to lib/rules/comment-whitespace-inside/__tests__/index.js index 2080bb8f90..0226df3498 100755 --- a/src/rules/comment-whitespace-inside/__tests__/index.js +++ b/lib/rules/comment-whitespace-inside/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/comment-whitespace-inside/index.js b/lib/rules/comment-whitespace-inside/index.js similarity index 70% rename from src/rules/comment-whitespace-inside/index.js rename to lib/rules/comment-whitespace-inside/index.js index 26b6030208..4bd838b9f6 100755 --- a/src/rules/comment-whitespace-inside/index.js +++ b/lib/rules/comment-whitespace-inside/index.js @@ -1,38 +1,41 @@ -import { - isWhitespace, - report, - ruleMessages, - validateOptions, -} from "../../utils" +"use strict" -export const ruleName = "comment-whitespace-inside" +const isWhitespace = require("../../utils/isWhitespace") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") -export const messages = ruleMessages(ruleName, { +const ruleName = "comment-whitespace-inside" + +const messages = ruleMessages(ruleName, { expectedOpening: "Expected whitespace after \"/*\"", rejectedOpening: "Unexpected whitespace after \"/*\"", expectedClosing: "Expected whitespace before \"*/\"", rejectedClosing: "Unexpected whitespace before \"*/\"", }) -export default function (expectation) { +const rule = function (expectation) { return function (root, result) { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - ], + possible: [ "always", "never" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkComments(function (comment) { - if (comment.raws.inline || comment.inline) { return } + if (comment.raws.inline || comment.inline) { + return + } const rawComment = comment.toString() const firstFourChars = rawComment.substr(0, 4) // Return early if sourcemap or copyright comment - if (/^\/\*[#!]\s/.test(firstFourChars)) { return } + if (/^\/\*[#!]\s/.test(firstFourChars)) { + return + } const leftMatches = rawComment.match(/(^\/\*+)(\s)?/) const rightMatches = rawComment.match(/(\s)?(\*+\/)$/) @@ -67,3 +70,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/comment-word-blacklist/README.md b/lib/rules/comment-word-blacklist/README.md similarity index 100% rename from src/rules/comment-word-blacklist/README.md rename to lib/rules/comment-word-blacklist/README.md diff --git a/src/rules/comment-word-blacklist/__tests__/index.js b/lib/rules/comment-word-blacklist/__tests__/index.js similarity index 92% rename from src/rules/comment-word-blacklist/__tests__/index.js rename to lib/rules/comment-word-blacklist/__tests__/index.js index 9bcf8fbd1b..c137d5ca18 100644 --- a/src/rules/comment-word-blacklist/__tests__/index.js +++ b/lib/rules/comment-word-blacklist/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -14,34 +14,29 @@ testRule(rule, { accept: [ { code: "/* comment */", }, { - code: "/*# bad-word */", // ignore sourcemaps - } ], + code: "/*# bad-word */" } ], reject: [ { code: "/* Comment with bad-word */", message: messages.rejected("bad-word"), line: 1, column: 1, - }, - { + }, { code: "/* bad-word */", message: messages.rejected("bad-word"), line: 1, column: 1, - }, - { + }, { code: "/*** bad-word ***/", message: messages.rejected("bad-word"), line: 1, column: 1, - }, - { + }, { code: "/*! bad-word */", message: messages.rejected("bad-word"), line: 1, column: 1, - }, - { + }, { code: "/** bad-word **/", message: messages.rejected("bad-word"), line: 1, @@ -51,10 +46,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "/^TODO:/", - "bad-word", - ]], + config: [[ "/^TODO:/", "bad-word" ]], accept: [ { code: "/* comment */", @@ -183,10 +175,7 @@ testRule(rule, { testRule(rule, { ruleName, syntax: "scss", - config: [[ - "/^TODO:/", - "bad-word", - ]], + config: [[ "/^TODO:/", "bad-word" ]], accept: [ { code: "// comment", @@ -221,10 +210,7 @@ testRule(rule, { testRule(rule, { ruleName, syntax: "less", - config: [[ - "/^TODO:/", - "bad-word", - ]], + config: [[ "/^TODO:/", "bad-word" ]], accept: [ { code: "// comment", diff --git a/lib/rules/comment-word-blacklist/index.js b/lib/rules/comment-word-blacklist/index.js new file mode 100644 index 0000000000..be93508677 --- /dev/null +++ b/lib/rules/comment-word-blacklist/index.js @@ -0,0 +1,56 @@ +"use strict" + +const containsString = require("../../utils/containsString") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") + +const ruleName = "comment-word-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: pattern => `Unexpected word matching pattern "${pattern}"`, +}) + +const rule = function (blacklist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkComments(comment => { + const text = comment.text + const rawComment = comment.toString() + const firstFourChars = rawComment.substr(0, 4) + + // Return early if sourcemap + if (firstFourChars === "/*# ") { + return + } + + const matchesWord = matchesStringOrRegExp(text, blacklist) || containsString(text, blacklist) + + if (!matchesWord) { + return + } + + report({ + message: messages.rejected(matchesWord.pattern), + node: comment, + result, + ruleName, + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/custom-media-pattern/README.md b/lib/rules/custom-media-pattern/README.md similarity index 100% rename from src/rules/custom-media-pattern/README.md rename to lib/rules/custom-media-pattern/README.md diff --git a/src/rules/custom-media-pattern/__tests__/index.js b/lib/rules/custom-media-pattern/__tests__/index.js similarity index 90% rename from src/rules/custom-media-pattern/__tests__/index.js rename to lib/rules/custom-media-pattern/__tests__/index.js index 157043776d..0918df570b 100644 --- a/src/rules/custom-media-pattern/__tests__/index.js +++ b/lib/rules/custom-media-pattern/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/custom-media-pattern/index.js b/lib/rules/custom-media-pattern/index.js new file mode 100644 index 0000000000..2ef6080560 --- /dev/null +++ b/lib/rules/custom-media-pattern/index.js @@ -0,0 +1,51 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") + +const ruleName = "custom-media-pattern" + +const messages = ruleMessages(ruleName, { + expected: "Expected custom media query name to match specified pattern", +}) + +const rule = function (pattern) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: pattern, + possible: [ _.isRegExp, _.isString ], + }) + if (!validOptions) { + return + } + + const regexpPattern = _.isString(pattern) ? new RegExp(pattern) : pattern + + root.walkAtRules(atRule => { + if (atRule.name.toLowerCase() !== "custom-media") { + return + } + + const customMediaName = atRule.params.match(/^--(\S+)\b/)[1] + + if (regexpPattern.test(customMediaName)) { + return + } + + report({ + message: messages.expected, + node: atRule, + index: atRuleParamIndex(atRule), + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/custom-property-empty-line-before/README.md b/lib/rules/custom-property-empty-line-before/README.md similarity index 100% rename from src/rules/custom-property-empty-line-before/README.md rename to lib/rules/custom-property-empty-line-before/README.md diff --git a/src/rules/custom-property-empty-line-before/__tests__/index.js b/lib/rules/custom-property-empty-line-before/__tests__/index.js similarity index 95% rename from src/rules/custom-property-empty-line-before/__tests__/index.js rename to lib/rules/custom-property-empty-line-before/__tests__/index.js index b79d6f2f1c..c7b3b3386e 100644 --- a/src/rules/custom-property-empty-line-before/__tests__/index.js +++ b/lib/rules/custom-property-empty-line-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -183,11 +183,7 @@ testRule(rule, { testRule(rule, { ruleName, config: [ "always", { - except: [ - "first-nested", - "after-comment", - "after-custom-property", - ] } ], + except: [ "first-nested", "after-comment", "after-custom-property" ] } ], accept: [{ code: "a {\n --custom-prop: value; \n --custom-prop2: value; \n /* comment */ \n --custom-prop3: value;\n\n @extends 'x';\n\n --custom-prop4: value; \n & b {\n prop: value;\n } \n\n --custom-prop5: value; \n }" }], @@ -317,11 +313,7 @@ testRule(rule, { testRule(rule, { ruleName, config: [ "never", { - except: [ - "first-nested", - "after-comment", - "after-custom-property", - ] } ], + except: [ "first-nested", "after-comment", "after-custom-property" ] } ], accept: [{ code: "a {\n\n --custom-prop: value; \n\n --custom-prop2: value; \n /* comment */ \n\n --custom-prop3: value;\n\n @extends 'x';\n --custom-prop4: value; \n & b {\n prop: value;\n } \n --custom-prop5: value; \n }" }], diff --git a/lib/rules/custom-property-empty-line-before/index.js b/lib/rules/custom-property-empty-line-before/index.js new file mode 100644 index 0000000000..87ec92cf3f --- /dev/null +++ b/lib/rules/custom-property-empty-line-before/index.js @@ -0,0 +1,95 @@ +"use strict" + +const blockString = require("../../utils/blockString") +const hasEmptyLine = require("../../utils/hasEmptyLine") +const isCustomProperty = require("../../utils/isCustomProperty") +const isSingleLineString = require("../../utils/isSingleLineString") +const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "custom-property-empty-line-before" + +const messages = ruleMessages(ruleName, { + expected: "Expected empty line before custom property", + rejected: "Unexpected empty line before custom property", +}) + +const rule = function (expectation, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }, { + actual: options, + possible: { + except: [ "first-nested", "after-comment", "after-custom-property" ], + ignore: [ "after-comment", "inside-single-line-block" ], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop, + parent = decl.parent + + if (!isStandardSyntaxDeclaration(decl)) { + return + } + if (!isCustomProperty(prop)) { + return + } + + // Optionally ignore the node if a comment precedes it + if (optionsMatches(options, "ignore", "after-comment") && decl.prev() && decl.prev().type === "comment") { + return + } + + // Optionally ignore nodes inside single-line blocks + if (optionsMatches(options, "ignore", "inside-single-line-block") && isSingleLineString(blockString(parent))) { + return + } + + let expectEmptyLineBefore = expectation === "always" ? true : false + + // Optionally reverse the expectation for the first nested node + if (optionsMatches(options, "except", "first-nested") && decl === parent.first) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Optionally reverse the expectation if a comment precedes this node + if (optionsMatches(options, "except", "after-comment") && decl.prev() && decl.prev().type === "comment") { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Optionally reverse the expectation if a custom property precedes this node + if (optionsMatches(options, "except", "after-custom-property") && decl.prev() && decl.prev().prop && isCustomProperty(decl.prev().prop)) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + const hasEmptyLineBefore = hasEmptyLine(decl.raws["before"]) + + // Return if the expectation is met + if (expectEmptyLineBefore === hasEmptyLineBefore) { + return + } + + const message = expectEmptyLineBefore ? messages.expected : messages.rejected + report({ + message, + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/custom-property-no-outside-root/README.md b/lib/rules/custom-property-no-outside-root/README.md similarity index 100% rename from src/rules/custom-property-no-outside-root/README.md rename to lib/rules/custom-property-no-outside-root/README.md diff --git a/src/rules/custom-property-no-outside-root/__tests__/index.js b/lib/rules/custom-property-no-outside-root/__tests__/index.js similarity index 81% rename from src/rules/custom-property-no-outside-root/__tests__/index.js rename to lib/rules/custom-property-no-outside-root/__tests__/index.js index 79b92f0480..fe599970c9 100644 --- a/src/rules/custom-property-no-outside-root/__tests__/index.js +++ b/lib/rules/custom-property-no-outside-root/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/custom-property-no-outside-root/index.js b/lib/rules/custom-property-no-outside-root/index.js new file mode 100644 index 0000000000..a8901f3632 --- /dev/null +++ b/lib/rules/custom-property-no-outside-root/index.js @@ -0,0 +1,44 @@ +"use strict" + +const isCustomProperty = require("../../utils/isCustomProperty") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "custom-property-no-outside-root" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected custom property", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + // Ignore rules whose selector is just `:root` + if (rule.selector.toLowerCase().trim() === ":root") { + return + } + + rule.walkDecls(decl => { + if (!isCustomProperty(decl.prop)) { + return + } + report({ + message: messages.rejected, + node: decl, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/custom-property-pattern/README.md b/lib/rules/custom-property-pattern/README.md similarity index 100% rename from src/rules/custom-property-pattern/README.md rename to lib/rules/custom-property-pattern/README.md diff --git a/src/rules/custom-property-pattern/__tests__/index.js b/lib/rules/custom-property-pattern/__tests__/index.js similarity index 85% rename from src/rules/custom-property-pattern/__tests__/index.js rename to lib/rules/custom-property-pattern/__tests__/index.js index 2e4d43de88..4c292c2b0f 100644 --- a/src/rules/custom-property-pattern/__tests__/index.js +++ b/lib/rules/custom-property-pattern/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/custom-property-pattern/index.js b/lib/rules/custom-property-pattern/index.js new file mode 100644 index 0000000000..d5a295825b --- /dev/null +++ b/lib/rules/custom-property-pattern/index.js @@ -0,0 +1,49 @@ +"use strict" + +const isCustomProperty = require("../../utils/isCustomProperty") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") + +const ruleName = "custom-property-pattern" + +const messages = ruleMessages(ruleName, { + expected: "Expected custom property name to match specified pattern", +}) + +const rule = function (pattern) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: pattern, + possible: [ _.isRegExp, _.isString ], + }) + if (!validOptions) { + return + } + + const regexpPattern = _.isString(pattern) ? new RegExp(pattern) : pattern + + root.walkDecls(decl => { + const prop = decl.prop + + if (!isCustomProperty(prop)) { + return + } + if (regexpPattern.test(prop.slice(2))) { + return + } + + report({ + message: messages.expected, + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-bang-space-after/README.md b/lib/rules/declaration-bang-space-after/README.md similarity index 100% rename from src/rules/declaration-bang-space-after/README.md rename to lib/rules/declaration-bang-space-after/README.md diff --git a/src/rules/declaration-bang-space-after/__tests__/index.js b/lib/rules/declaration-bang-space-after/__tests__/index.js similarity index 92% rename from src/rules/declaration-bang-space-after/__tests__/index.js rename to lib/rules/declaration-bang-space-after/__tests__/index.js index 2b0e97c9ea..1d93c4afc6 100644 --- a/src/rules/declaration-bang-space-after/__tests__/index.js +++ b/lib/rules/declaration-bang-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-bang-space-after/index.js b/lib/rules/declaration-bang-space-after/index.js new file mode 100644 index 0000000000..ee9c83dec7 --- /dev/null +++ b/lib/rules/declaration-bang-space-after/index.js @@ -0,0 +1,37 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const declarationBangSpaceChecker = require("../declarationBangSpaceChecker") + +const ruleName = "declaration-bang-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected single space after \"!\"", + rejectedAfter: () => "Unexpected whitespace after \"!\"", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + declarationBangSpaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-bang-space-before/README.md b/lib/rules/declaration-bang-space-before/README.md similarity index 100% rename from src/rules/declaration-bang-space-before/README.md rename to lib/rules/declaration-bang-space-before/README.md diff --git a/src/rules/declaration-bang-space-before/__tests__/index.js b/lib/rules/declaration-bang-space-before/__tests__/index.js similarity index 92% rename from src/rules/declaration-bang-space-before/__tests__/index.js rename to lib/rules/declaration-bang-space-before/__tests__/index.js index c866b87204..318b5e280f 100644 --- a/src/rules/declaration-bang-space-before/__tests__/index.js +++ b/lib/rules/declaration-bang-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-bang-space-before/index.js b/lib/rules/declaration-bang-space-before/index.js new file mode 100644 index 0000000000..cef4201979 --- /dev/null +++ b/lib/rules/declaration-bang-space-before/index.js @@ -0,0 +1,37 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const declarationBangSpaceChecker = require("../declarationBangSpaceChecker") + +const ruleName = "declaration-bang-space-before" + +const messages = ruleMessages(ruleName, { + expectedBefore: () => "Expected single space before \"!\"", + rejectedBefore: () => "Unexpected whitespace before \"!\"", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + declarationBangSpaceChecker({ + root, + result, + locationChecker: checker.before, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-no-duplicate-properties/README.md b/lib/rules/declaration-block-no-duplicate-properties/README.md similarity index 100% rename from src/rules/declaration-block-no-duplicate-properties/README.md rename to lib/rules/declaration-block-no-duplicate-properties/README.md diff --git a/src/rules/declaration-block-no-duplicate-properties/__tests__/index.js b/lib/rules/declaration-block-no-duplicate-properties/__tests__/index.js similarity index 96% rename from src/rules/declaration-block-no-duplicate-properties/__tests__/index.js rename to lib/rules/declaration-block-no-duplicate-properties/__tests__/index.js index 15e53b2cfa..c4a539c8cc 100644 --- a/src/rules/declaration-block-no-duplicate-properties/__tests__/index.js +++ b/lib/rules/declaration-block-no-duplicate-properties/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/declaration-block-no-duplicate-properties/index.js b/lib/rules/declaration-block-no-duplicate-properties/index.js similarity index 63% rename from src/rules/declaration-block-no-duplicate-properties/index.js rename to lib/rules/declaration-block-no-duplicate-properties/index.js index cd8dc0f34f..f5f9880fe3 100644 --- a/src/rules/declaration-block-no-duplicate-properties/index.js +++ b/lib/rules/declaration-block-no-duplicate-properties/index.js @@ -1,33 +1,32 @@ -import { - isCustomProperty, - isStandardSyntaxProperty, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" - -export const ruleName = "declaration-block-no-duplicate-properties" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const isCustomProperty = require("../../utils/isCustomProperty") +const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") + +const ruleName = "declaration-block-no-duplicate-properties" + +const messages = ruleMessages(ruleName, { rejected: property => `Unexpected duplicate "${property}"`, }) -export default function (on, options) { +const rule = function (on, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: on }, { actual: options, possible: { - ignore: [ - "consecutive-duplicates", - "consecutive-duplicates-with-different-values", - ], - ignoreProperties: [isString], + ignore: [ "consecutive-duplicates", "consecutive-duplicates-with-different-values" ], + ignoreProperties: [_.isString], }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } // In order to accommodate nested blocks (postcss-nested), // we need to run a shallow loop (instead of eachDecl() or eachRule(), @@ -49,19 +48,29 @@ export default function (on, options) { checkRulesInNode(child) } - if (child.type !== "decl") { return } + if (child.type !== "decl") { + return + } - const { prop } = child - const { value } = child + const prop = child.prop + const value = child.value - if (!isStandardSyntaxProperty(prop)) { return } - if (isCustomProperty(prop)) { return } + if (!isStandardSyntaxProperty(prop)) { + return + } + if (isCustomProperty(prop)) { + return + } // Return early if the property is to be ignored - if (optionsMatches(options, "ignoreProperties", prop)) { return } + if (optionsMatches(options, "ignoreProperties", prop)) { + return + } // Ignore the src property as commonly duplicated in at-fontface - if (prop.toLowerCase() === "src") { return } + if (prop.toLowerCase() === "src") { + return + } const indexDuplicate = decls.indexOf(prop.toLowerCase()) @@ -90,10 +99,7 @@ export default function (on, options) { return } - if ( - optionsMatches(options, "ignore", "consecutive-duplicates") - && indexDuplicate === decls.length - 1 - ) { + if (optionsMatches(options, "ignore", "consecutive-duplicates") && indexDuplicate === decls.length - 1) { return } @@ -111,3 +117,7 @@ export default function (on, options) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-no-ignored-properties/README.md b/lib/rules/declaration-block-no-ignored-properties/README.md similarity index 100% rename from src/rules/declaration-block-no-ignored-properties/README.md rename to lib/rules/declaration-block-no-ignored-properties/README.md diff --git a/src/rules/declaration-block-no-ignored-properties/__tests__/index.js b/lib/rules/declaration-block-no-ignored-properties/__tests__/index.js similarity index 98% rename from src/rules/declaration-block-no-ignored-properties/__tests__/index.js rename to lib/rules/declaration-block-no-ignored-properties/__tests__/index.js index 897bc7d2a1..4d0b3b5b02 100644 --- a/src/rules/declaration-block-no-ignored-properties/__tests__/index.js +++ b/lib/rules/declaration-block-no-ignored-properties/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -139,8 +139,7 @@ testRule(rule, { line: 1, column: 22, description: "display: inline rules out min-height", - }, - { + }, { code: "a { display: inline; max-height: 100px; }", message: messages.rejected("max-height", "display: inline"), line: 1, @@ -314,36 +313,31 @@ testRule(rule, { line: 1, column: 25, description: "display: table-row rules out width", - }, - { + }, { code: "a { display: table-row; min-width: 10px; }", message: messages.rejected("min-width", "display: table-row"), line: 1, column: 25, description: "display: table-row rules out min-width", - }, - { + }, { code: "a { display: table-row; max-width: 10px; }", message: messages.rejected("max-width", "display: table-row"), line: 1, column: 25, description: "display: table-row rules out max-width", - }, - { + }, { code: "a { display: table-row-group; width: 10px; }", message: messages.rejected("width", "display: table-row-group"), line: 1, column: 31, description: "display: table-row-group rules out width", - }, - { + }, { code: "a { display: table-row-group; min-width: 10px; }", message: messages.rejected("min-width", "display: table-row-group"), line: 1, column: 31, description: "display: table-row-group rules out min-width", - }, - { + }, { code: "a { display: table-row-group; max-width: 10px; }", message: messages.rejected("max-width", "display: table-row-group"), line: 1, @@ -355,15 +349,13 @@ testRule(rule, { line: 1, column: 28, description: "display: table-column rules out height", - }, - { + }, { code: "a { display: table-column; min-height: 10px; }", message: messages.rejected("min-height", "display: table-column"), line: 1, column: 28, description: "display: table-column rules out min-width", - }, - { + }, { code: "a { display: table-column; max-height: 10px; }", message: messages.rejected("max-height", "display: table-column"), line: 1, @@ -375,15 +367,13 @@ testRule(rule, { line: 1, column: 34, description: "display: table-column-group rules out height", - }, - { + }, { code: "a { display: table-column-group; min-height: 10px; }", message: messages.rejected("min-height", "display: table-column-group"), line: 1, column: 34, description: "display: table-column-group rules out min-height", - }, - { + }, { code: "a { display: table-column-group; max-height: 10px; }", message: messages.rejected("max-height", "display: table-column-group"), line: 1, diff --git a/src/rules/declaration-block-no-ignored-properties/index.js b/lib/rules/declaration-block-no-ignored-properties/index.js similarity index 50% rename from src/rules/declaration-block-no-ignored-properties/index.js rename to lib/rules/declaration-block-no-ignored-properties/index.js index 8a97e8d611..d3627ce7ce 100644 --- a/src/rules/declaration-block-no-ignored-properties/index.js +++ b/lib/rules/declaration-block-no-ignored-properties/index.js @@ -1,155 +1,107 @@ -import { - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { vendor } from "postcss" +"use strict" -export const ruleName = "declaration-block-no-ignored-properties" +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const postcss = require("postcss") -export const messages = ruleMessages(ruleName, { +const ruleName = "declaration-block-no-ignored-properties" + +const messages = ruleMessages(ruleName, { rejected: (ignored, cause) => `Unexpected "${ignored}" with "${cause}"`, }) const ignored = [ { property: "display", value: "inline", - ignoredProperties: [ - "width", - "min-width", - "max-width", - "height", - "min-height", - "max-height", - "margin", - "margin-top", - "margin-bottom", - "overflow", - "overflow-x", - "overflow-y", - ], + ignoredProperties: [ "width", "min-width", "max-width", "height", "min-height", "max-height", "margin", "margin-top", "margin-bottom", "overflow", "overflow-x", "overflow-y" ], }, { property: "display", value: "list-item", - ignoredProperties: [ - "vertical-align", - ], + ignoredProperties: ["vertical-align"], }, { property: "display", value: "block", - ignoredProperties: [ - "vertical-align", - ], + ignoredProperties: ["vertical-align"], }, { property: "display", value: "flex", - ignoredProperties: [ - "vertical-align", - ], + ignoredProperties: ["vertical-align"], }, { property: "display", value: "table", - ignoredProperties: [ - "vertical-align", - ], + ignoredProperties: ["vertical-align"], }, { property: "display", value: "/^table-.*$/", - ignoredProperties: [ - "margin", - "margin-top", - "margin-right", - "margin-bottom", - "margin-left", - ], + ignoredProperties: [ "margin", "margin-top", "margin-right", "margin-bottom", "margin-left" ], }, { property: "display", value: "/^table-(row|row-group|column|column-group|header-group|footer-group|caption).*$/", - ignoredProperties: [ - "vertical-align", - ], + ignoredProperties: ["vertical-align"], }, { property: "display", value: "/^table-(row|row-group).*$/", - ignoredProperties: [ - "width", - "min-width", - "max-width", - ], + ignoredProperties: [ "width", "min-width", "max-width" ], }, { property: "display", value: "/^table-(column|column-group).*$/", - ignoredProperties: [ - "height", - "min-height", - "max-height", - ], + ignoredProperties: [ "height", "min-height", "max-height" ], }, { property: "float", value: "left", - ignoredProperties: [ - "vertical-align", - ], + ignoredProperties: ["vertical-align"], }, { property: "float", value: "right", - ignoredProperties: [ - "vertical-align", - ], + ignoredProperties: ["vertical-align"], }, { property: "position", value: "static", - ignoredProperties: [ - "top", - "right", - "bottom", - "left", - ], + ignoredProperties: [ "top", "right", "bottom", "left" ], }, { property: "position", value: "absolute", - ignoredProperties: [ - "float", - "clear", - "vertical-align", - ], + ignoredProperties: [ "float", "clear", "vertical-align" ], }, { property: "position", value: "fixed", - ignoredProperties: [ - "float", - "clear", - "vertical-align", - ], + ignoredProperties: [ "float", "clear", "vertical-align" ], } ] -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } const uniqueDecls = {} - root.walkDecls((decl) => { + root.walkDecls(decl => { uniqueDecls[decl.prop] = decl }) Object.keys(uniqueDecls).forEach((prop, index) => { const decl = uniqueDecls[prop] - const unprefixedProp = vendor.unprefixed(prop) - const unprefixedValue = vendor.unprefixed(decl.value) + const unprefixedProp = postcss.vendor.unprefixed(prop) + const unprefixedValue = postcss.vendor.unprefixed(decl.value) ignored.forEach(ignore => { const matchProperty = matchesStringOrRegExp(unprefixedProp.toLowerCase(), ignore.property) const matchValue = matchesStringOrRegExp(unprefixedValue.toLowerCase(), ignore.value) - if (!matchProperty || !matchValue) { return } + if (!matchProperty || !matchValue) { + return + } const ignoredProperties = ignore.ignoredProperties decl.parent.nodes.forEach((node, nodeIndex) => { - if (!node.prop || ignoredProperties.indexOf(node.prop.toLowerCase()) === -1 || index === nodeIndex) { return } + if (!node.prop || ignoredProperties.indexOf(node.prop.toLowerCase()) === -1 || index === nodeIndex) { + return + } report({ message: messages.rejected(node.prop, decl.toString()), @@ -162,3 +114,7 @@ export default function (actual) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-no-redundant-longhand-properties/README.md b/lib/rules/declaration-block-no-redundant-longhand-properties/README.md similarity index 100% rename from src/rules/declaration-block-no-redundant-longhand-properties/README.md rename to lib/rules/declaration-block-no-redundant-longhand-properties/README.md diff --git a/src/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js b/lib/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js similarity index 92% rename from src/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js rename to lib/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js index 4ab797b2ff..1117bff38f 100644 --- a/src/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js +++ b/lib/rules/declaration-block-no-redundant-longhand-properties/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-block-no-redundant-longhand-properties/index.js b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js new file mode 100644 index 0000000000..e15fc9c5bc --- /dev/null +++ b/lib/rules/declaration-block-no-redundant-longhand-properties/index.js @@ -0,0 +1,80 @@ +"use strict" + +const _ = require("lodash") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const shorthandData = require("../../reference/shorthandData") + +const ruleName = "declaration-block-no-redundant-longhand-properties" + +const messages = ruleMessages(ruleName, { + expected: props => `Expected shorthand property "${props}"`, +}) + +const rule = function (actual, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }, { + actual: options, + possible: { + ignoreShorthands: [_.isString], + }, + optional: true, + }) + if (!validOptions) { + return + } + + const longhandProperties = _.transform(shorthandData, (result, values, key) => { + if (optionsMatches(options, "ignoreShorthands", key)) { + return + } + + values.forEach(value => { + (result[value] || (result[value] = [])).push(key) + }) + }) + + root.walkRules(check) + root.walkAtRules(check) + + function check(statement) { + const longhandDeclarations = {} + // Shallow iteration so nesting doesn't produce + // false positives + statement.each(node => { + if (node.type !== "decl") { + return + } + + const prop = node.prop.toLowerCase() + + const shorthandProperties = longhandProperties[prop] + + if (!shorthandProperties) { + return + } + + shorthandProperties.forEach(shorthandProperty => { + (longhandDeclarations[shorthandProperty] || (longhandDeclarations[shorthandProperty] = [])).push(prop) + + if (!_.isEqual(shorthandData[shorthandProperty].sort(), longhandDeclarations[shorthandProperty].sort())) { + return + } + + report({ + ruleName, + result, + node, + message: messages.expected(shorthandProperty), + }) + }) + }) + } + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-no-shorthand-property-overrides/README.md b/lib/rules/declaration-block-no-shorthand-property-overrides/README.md similarity index 100% rename from src/rules/declaration-block-no-shorthand-property-overrides/README.md rename to lib/rules/declaration-block-no-shorthand-property-overrides/README.md diff --git a/src/rules/declaration-block-no-shorthand-property-overrides/__tests__/index.js b/lib/rules/declaration-block-no-shorthand-property-overrides/__tests__/index.js similarity index 91% rename from src/rules/declaration-block-no-shorthand-property-overrides/__tests__/index.js rename to lib/rules/declaration-block-no-shorthand-property-overrides/__tests__/index.js index 6311af3f51..62028ea951 100644 --- a/src/rules/declaration-block-no-shorthand-property-overrides/__tests__/index.js +++ b/lib/rules/declaration-block-no-shorthand-property-overrides/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-block-no-shorthand-property-overrides/index.js b/lib/rules/declaration-block-no-shorthand-property-overrides/index.js new file mode 100644 index 0000000000..16049ac090 --- /dev/null +++ b/lib/rules/declaration-block-no-shorthand-property-overrides/index.js @@ -0,0 +1,57 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const shorthandData = require("../../reference/shorthandData") + +const ruleName = "declaration-block-no-shorthand-property-overrides" + +const messages = ruleMessages(ruleName, { + rejected: (shorthand, original) => `Unexpected shorthand "${shorthand}" after "${original}"`, +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(check) + root.walkAtRules(check) + + function check(statement) { + const declarations = {} + // Shallow iteration so nesting doesn't produce + // false positives + statement.each(node => { + if (node.type !== "decl") { + return + } + const prop = node.prop + + const overrideables = shorthandData[prop.toLowerCase()] + if (!overrideables) { + declarations[prop.toLowerCase()] = prop + return + } + overrideables.forEach(longhandProp => { + if (!declarations.hasOwnProperty(longhandProp)) { + return + } + report({ + ruleName, + result, + node, + message: messages.rejected(prop, declarations[longhandProp]), + }) + }) + }) + } + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-properties-order/README.md b/lib/rules/declaration-block-properties-order/README.md similarity index 100% rename from src/rules/declaration-block-properties-order/README.md rename to lib/rules/declaration-block-properties-order/README.md diff --git a/src/rules/declaration-block-properties-order/__tests__/alphabetical.js b/lib/rules/declaration-block-properties-order/__tests__/alphabetical.js similarity index 93% rename from src/rules/declaration-block-properties-order/__tests__/alphabetical.js rename to lib/rules/declaration-block-properties-order/__tests__/alphabetical.js index b60187e92d..57387ecd6c 100644 --- a/src/rules/declaration-block-properties-order/__tests__/alphabetical.js +++ b/lib/rules/declaration-block-properties-order/__tests__/alphabetical.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/declaration-block-properties-order/__tests__/flat.js b/lib/rules/declaration-block-properties-order/__tests__/flat.js similarity index 89% rename from src/rules/declaration-block-properties-order/__tests__/flat.js rename to lib/rules/declaration-block-properties-order/__tests__/flat.js index c982edcadc..5eb4950f5e 100644 --- a/src/rules/declaration-block-properties-order/__tests__/flat.js +++ b/lib/rules/declaration-block-properties-order/__tests__/flat.js @@ -1,24 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "my", - "transform", - "font-smoothing", - "top", - "transition", - "border", - "color", - ]], + config: [[ "my", "transform", "font-smoothing", "top", "transition", "border", "color" ]], accept: [ { code: "a { color: pink; }", @@ -104,16 +96,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "padding", - "padding-top", - "padding-right", - "padding-left", - "border", - "border-top", - "border-right", - "color", - ]], + config: [[ "padding", "padding-top", "padding-right", "padding-left", "border", "border-top", "border-right", "color" ]], accept: [ { code: "a { padding: 1px; color: pink; }", @@ -148,10 +131,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [ [ - "height", - "color", - ], { unspecified: "top" } ], + config: [ [ "height", "color" ], { unspecified: "top" } ], accept: [ { code: "a { top: 0; height: 1px; color: pink; }", @@ -171,10 +151,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [ [ - "height", - "color", - ], { unspecified: "bottom" } ], + config: [ [ "height", "color" ], { unspecified: "bottom" } ], accept: [ { code: "a { height: 1px; color: pink; bottom: 0; }", @@ -194,10 +171,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [ [ - "all", - "compose", - ], { unspecified: "bottomAlphabetical" } ], + config: [ [ "all", "compose" ], { unspecified: "bottomAlphabetical" } ], accept: [ { code: "a { all: initial; compose: b; }", @@ -219,10 +193,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "left", - "margin", - ]], + config: [[ "left", "margin" ]], skipBasicChecks: true, diff --git a/src/rules/declaration-block-properties-order/__tests__/grouped-flexible.js b/lib/rules/declaration-block-properties-order/__tests__/grouped-flexible.js similarity index 82% rename from src/rules/declaration-block-properties-order/__tests__/grouped-flexible.js rename to lib/rules/declaration-block-properties-order/__tests__/grouped-flexible.js index 18ff9398b6..36a614a6cd 100644 --- a/src/rules/declaration-block-properties-order/__tests__/grouped-flexible.js +++ b/lib/rules/declaration-block-properties-order/__tests__/grouped-flexible.js @@ -1,27 +1,19 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "height", - "width", - { - order: "flexible", - properties: [ - "color", - "font-size", - "font-weight", - ], - }, - ]], + config: [[ "height", "width", { + order: "flexible", + properties: [ "color", "font-size", "font-weight" ], + } ]], accept: [ { code: "a { height: 1px; width: 2px; color: pink; font-size: 2px; font-weight: bold; }", @@ -71,23 +63,13 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - { - order: "flexible", - properties: [ - "width", - "height", - ], - }, - { - order: "flexible", - properties: [ - "color", - "font-size", - "font-weight", - ], - }, - ]], + config: [[ { + order: "flexible", + properties: [ "width", "height" ], + }, { + order: "flexible", + properties: [ "color", "font-size", "font-weight" ], + } ]], accept: [ { code: "a { height: 1px; width: 2px; color: pink; font-size: 2px; font-weight: bold; }", diff --git a/src/rules/declaration-block-properties-order/__tests__/grouped-strict.js b/lib/rules/declaration-block-properties-order/__tests__/grouped-strict.js similarity index 81% rename from src/rules/declaration-block-properties-order/__tests__/grouped-strict.js rename to lib/rules/declaration-block-properties-order/__tests__/grouped-strict.js index 57a1df1915..82672e2fe8 100644 --- a/src/rules/declaration-block-properties-order/__tests__/grouped-strict.js +++ b/lib/rules/declaration-block-properties-order/__tests__/grouped-strict.js @@ -1,27 +1,19 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "height", - "width", - { - order: "strict", - properties: [ - "color", - "font-size", - "font-weight", - ], - }, - ]], + config: [[ "height", "width", { + order: "strict", + properties: [ "color", "font-size", "font-weight" ], + } ]], accept: [ { code: "a { height: 1px; width: 2px; color: pink; font-size: 2px; font-weight: bold; }", @@ -60,23 +52,13 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - { - order: "strict", - properties: [ - "width", - "height", - ], - }, - { - order: "strict", - properties: [ - "color", - "font-size", - "font-weight", - ], - }, - ]], + config: [[ { + order: "strict", + properties: [ "width", "height" ], + }, { + order: "strict", + properties: [ "color", "font-size", "font-weight" ], + } ]], accept: [{ code: "a { width: 2px; height: 1px; color: pink; font-size: 2px; font-weight: bold; }", diff --git a/src/rules/declaration-block-properties-order/__tests__/validate-options.js b/lib/rules/declaration-block-properties-order/__tests__/validate-options.js similarity index 70% rename from src/rules/declaration-block-properties-order/__tests__/validate-options.js rename to lib/rules/declaration-block-properties-order/__tests__/validate-options.js index cc848c9465..2b09a0a902 100644 --- a/src/rules/declaration-block-properties-order/__tests__/validate-options.js +++ b/lib/rules/declaration-block-properties-order/__tests__/validate-options.js @@ -1,13 +1,13 @@ -import stylelint from "../../.." -import test from "tape" +"use strict" + +const stylelint = require("../../..") +const test = require("tape") test("valid default order", t => { const config = { rules: { "declaration-block-properties-order": [{ - properties: [ - "color", - ], + properties: ["color"], }], }, } @@ -26,9 +26,7 @@ test("valid 'strict' order", t => { rules: { "declaration-block-properties-order": [{ order: "strict", - properties: [ - "color", - ], + properties: ["color"], }], }, } @@ -47,9 +45,7 @@ test("valid 'flexible' order", t => { rules: { "declaration-block-properties-order": [{ order: "flexible", - properties: [ - "color", - ], + properties: ["color"], }], }, } @@ -66,14 +62,9 @@ test("valid 'flexible' order", t => { test("valid order with a group and one outside property before the group", t => { const config = { rules: { - "declaration-block-properties-order": [ - "height", - { - properties: [ - "color", - ], - }, - ], + "declaration-block-properties-order": [ "height", { + properties: ["color"], + } ], }, } stylelint.lint({ @@ -90,14 +81,9 @@ test("valid order with a group and one outside property before the group", t => test("valid order with a group and one outside property after the group", t => { const config = { rules: { - "declaration-block-properties-order": [ - { - properties: [ - "color", - ], - }, - "height", - ], + "declaration-block-properties-order": [ { + properties: ["color"], + }, "height" ], }, } stylelint.lint({ @@ -114,15 +100,9 @@ test("valid order with a group and one outside property after the group", t => { test("valid order with a group and two outside properties before the group", t => { const config = { rules: { - "declaration-block-properties-order": [ - "height", - "width", - { - properties: [ - "color", - ], - }, - ], + "declaration-block-properties-order": [ "height", "width", { + properties: ["color"], + } ], }, } stylelint.lint({ @@ -139,19 +119,11 @@ test("valid order with a group and two outside properties before the group", t = test("valid order with groups and one outside property before groups", t => { const config = { rules: { - "declaration-block-properties-order": [ - "height", - { - properties: [ - "color", - ], - }, - { - properties: [ - "width", - ], - }, - ], + "declaration-block-properties-order": [ "height", { + properties: ["color"], + }, { + properties: ["width"], + } ], }, } stylelint.lint({ @@ -170,9 +142,7 @@ test("invalid option order option", t => { rules: { "declaration-block-properties-order": [{ order: "unknown-keyword", - properties: [ - "color", - ], + properties: ["color"], }], }, } @@ -182,10 +152,7 @@ test("invalid option order option", t => { }).then(function (data) { const invalidOptionWarnings = data.results[0].invalidOptionWarnings t.equal(invalidOptionWarnings.length, 1) - t.equal( - invalidOptionWarnings[0].text, - "Invalid option \"[{\"order\":\"unknown-keyword\",\"properties\":[\"color\"]}]\" for rule declaration-block-properties-order" - ) + t.equal(invalidOptionWarnings[0].text, "Invalid option \"[{\"order\":\"unknown-keyword\",\"properties\":[\"color\"]}]\" for rule declaration-block-properties-order") t.end() }).catch(t.end) }) @@ -204,10 +171,7 @@ test("invalid object lacks 'properties' property", t => { }).then(function (data) { const invalidOptionWarnings = data.results[0].invalidOptionWarnings t.equal(invalidOptionWarnings.length, 1) - t.equal( - invalidOptionWarnings[0].text, - "Invalid option \"[{\"order\":\"flexible\"}]\" for rule declaration-block-properties-order" - ) + t.equal(invalidOptionWarnings[0].text, "Invalid option \"[{\"order\":\"flexible\"}]\" for rule declaration-block-properties-order") t.end() }).catch(t.end) }) @@ -216,9 +180,7 @@ test("invalid object outside of array", t => { const config = { rules: { "declaration-block-properties-order": { - properties: [ - "color", - ], + properties: ["color"], }, }, } @@ -228,10 +190,7 @@ test("invalid object outside of array", t => { }).then(function (data) { const invalidOptionWarnings = data.results[0].invalidOptionWarnings t.equal(invalidOptionWarnings.length, 1) - t.equal( - invalidOptionWarnings[0].text, - "Invalid option \"{\"properties\":[\"color\"]}\" for rule declaration-block-properties-order" - ) + t.equal(invalidOptionWarnings[0].text, "Invalid option \"{\"properties\":[\"color\"]}\" for rule declaration-block-properties-order") t.end() }).catch(t.end) }) diff --git a/src/rules/declaration-block-properties-order/index.js b/lib/rules/declaration-block-properties-order/index.js similarity index 68% rename from src/rules/declaration-block-properties-order/index.js rename to lib/rules/declaration-block-properties-order/index.js index 00d49aedde..367460ea8c 100644 --- a/src/rules/declaration-block-properties-order/index.js +++ b/lib/rules/declaration-block-properties-order/index.js @@ -1,20 +1,20 @@ -import { - isCustomProperty, - isStandardSyntaxProperty, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" -import { vendor } from "postcss" - -export const ruleName = "declaration-block-properties-order" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const isCustomProperty = require("../../utils/isCustomProperty") +const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "declaration-block-properties-order" + +const messages = ruleMessages(ruleName, { expected: (first, second) => `Expected "${first}" to come before "${second}"`, }) -function rule(expectation, options) { +const rule = function (expectation, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, @@ -26,10 +26,12 @@ function rule(expectation, options) { }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } const alphabetical = expectation === "alphabetical" - const expectedOrder = (alphabetical) ? null : createExpectedOrder(expectation) + const expectedOrder = alphabetical ? null : createExpectedOrder(expectation) // By default, ignore unspecified properties const unspecified = _.get(options, ["unspecified"], "ignore") @@ -51,13 +53,20 @@ function rule(expectation, options) { checkNode(child) } - if (child.type !== "decl") { return } + if (child.type !== "decl") { + return + } + + const prop = child.prop - const { prop } = child - if (!isStandardSyntaxProperty(prop)) { return } - if (isCustomProperty(prop)) { return } + if (!isStandardSyntaxProperty(prop)) { + return + } + if (isCustomProperty(prop)) { + return + } - let unprefixedPropName = vendor.unprefixed(prop) + let unprefixedPropName = postcss.vendor.unprefixed(prop) // Hack to allow -moz-osx-font-smoothing to be understood // just like -webkit-font-smoothing @@ -68,7 +77,7 @@ function rule(expectation, options) { const propData = { name: prop, unprefixedName: unprefixedPropName, - orderData: (alphabetical) ? null : getOrderData(expectedOrder, unprefixedPropName), + orderData: alphabetical ? null : getOrderData(expectedOrder, unprefixedPropName), before: child.raws.before, index: allPropData.length, node: child, @@ -78,13 +87,15 @@ function rule(expectation, options) { allPropData.push(propData) // Skip first decl - if (!previousPropData) { return } + if (!previousPropData) { + return + } - const isCorrectOrder = (alphabetical) - ? checkAlpabeticalOrder(previousPropData, propData) - : checkOrder(previousPropData, propData) + const isCorrectOrder = alphabetical ? checkAlpabeticalOrder(previousPropData, propData) : checkOrder(previousPropData, propData) - if (isCorrectOrder) { return } + if (isCorrectOrder) { + return + } complain({ message: messages.expected(propData.name, previousPropData.name), @@ -110,10 +121,7 @@ function rule(expectation, options) { // If first prop is unspecified, look for a specified prop before it to // compare to the current prop const priorSpecifiedPropData = _.findLast(allPropData.slice(0, -1), d => !!d.orderData) - if ( - priorSpecifiedPropData && priorSpecifiedPropData.orderData - && priorSpecifiedPropData.orderData.expectedPosition > secondPropData.orderData.expectedPosition - ) { + if (priorSpecifiedPropData && priorSpecifiedPropData.orderData && priorSpecifiedPropData.orderData.expectedPosition > secondPropData.orderData.expectedPosition) { complain({ message: messages.expected(secondPropData.name, priorSpecifiedPropData.name), node: secondPropData.node, @@ -124,33 +132,49 @@ function rule(expectation, options) { // Now deal with unspecified props ... // Starting with bottomAlphabetical as it requires more specific conditionals - if (unspecified === "bottomAlphabetical" && !firstPropIsUnspecified && - secondPropIsUnspecified) { return true } + if (unspecified === "bottomAlphabetical" && !firstPropIsUnspecified && secondPropIsUnspecified) { + return true + } - if (unspecified === "bottomAlphabetical" && - secondPropIsUnspecified && - firstPropIsUnspecified) { - if (checkAlpabeticalOrder(firstPropData, secondPropData)) { return true } - else { return false } + if (unspecified === "bottomAlphabetical" && secondPropIsUnspecified && firstPropIsUnspecified) { + if (checkAlpabeticalOrder(firstPropData, secondPropData)) { + return true + } else { + return false + } + } + if (unspecified === "bottomAlphabetical" && firstPropIsUnspecified) { + return false } - if (unspecified === "bottomAlphabetical" && firstPropIsUnspecified) { return false } - if (firstPropIsUnspecified && secondPropIsUnspecified) { return true } + if (firstPropIsUnspecified && secondPropIsUnspecified) { + return true + } - if (unspecified === "ignore" && (firstPropIsUnspecified || secondPropIsUnspecified)) { return true } + if (unspecified === "ignore" && (firstPropIsUnspecified || secondPropIsUnspecified)) { + return true + } - if (unspecified === "top" && firstPropIsUnspecified) { return true } - if (unspecified === "top" && secondPropIsUnspecified) { return false } + if (unspecified === "top" && firstPropIsUnspecified) { + return true + } + if (unspecified === "top" && secondPropIsUnspecified) { + return false + } - if (unspecified === "bottom" && secondPropIsUnspecified) { return true } - if (unspecified === "bottom" && firstPropIsUnspecified) { return false } + if (unspecified === "bottom" && secondPropIsUnspecified) { + return true + } + if (unspecified === "bottom" && firstPropIsUnspecified) { + return false + } } } - function complain({ message, node }) { + function complain(opts) { report({ - message, - node, + message: opts.message, + node: opts.node, result, ruleName, }) @@ -160,7 +184,9 @@ function rule(expectation, options) { rule.primaryOptionArray = true -export default rule +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule function createExpectedOrder(input) { const order = {} @@ -177,7 +203,9 @@ function createExpectedOrder(input) { // In flexible groups, the expectedPosition does not ascend // to make that flexibility work; // otherwise, it will always ascend - if (!inFlexibleGroup) { expectedPosition += 1 } + if (!inFlexibleGroup) { + expectedPosition += 1 + } order[item] = { expectedPosition } return } @@ -219,25 +247,37 @@ function checkAlpabeticalOrder(firstPropData, secondPropData) { function validatePrimaryOption(actualOptions) { // Return true early if alphabetical - if (actualOptions === "alphabetical") { return true } + if (actualOptions === "alphabetical") { + return true + } // Otherwise, begin checking array options - if (!Array.isArray(actualOptions)) { return false } + if (!Array.isArray(actualOptions)) { + return false + } // Every item in the array must be a string or an object // with a "properties" property if (!actualOptions.every(item => { - if (_.isString(item)) { return true } + if (_.isString(item)) { + return true + } return _.isPlainObject(item) && !_.isUndefined(item.properties) - })) { return false } + })) { + return false + } const objectItems = actualOptions.filter(_.isPlainObject) // Every object-item's "order" property must be "strict" or "flexible" if (!objectItems.every(item => { - if (_.isUndefined(item.order)) { return true } + if (_.isUndefined(item.order)) { + return true + } return _.includes([ "strict", "flexible" ], item.order) - })) { return false } + })) { + return false + } return true } diff --git a/src/rules/declaration-block-semicolon-newline-after/README.md b/lib/rules/declaration-block-semicolon-newline-after/README.md similarity index 100% rename from src/rules/declaration-block-semicolon-newline-after/README.md rename to lib/rules/declaration-block-semicolon-newline-after/README.md diff --git a/src/rules/declaration-block-semicolon-newline-after/__tests__/index.js b/lib/rules/declaration-block-semicolon-newline-after/__tests__/index.js similarity index 96% rename from src/rules/declaration-block-semicolon-newline-after/__tests__/index.js rename to lib/rules/declaration-block-semicolon-newline-after/__tests__/index.js index cb8b696352..e38bc442a9 100644 --- a/src/rules/declaration-block-semicolon-newline-after/__tests__/index.js +++ b/lib/rules/declaration-block-semicolon-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/declaration-block-semicolon-newline-after/index.js b/lib/rules/declaration-block-semicolon-newline-after/index.js similarity index 56% rename from src/rules/declaration-block-semicolon-newline-after/index.js rename to lib/rules/declaration-block-semicolon-newline-after/index.js index 4252a6b173..0d15cf0939 100644 --- a/src/rules/declaration-block-semicolon-newline-after/index.js +++ b/lib/rules/declaration-block-semicolon-newline-after/index.js @@ -1,46 +1,50 @@ -import { - blockString, - nextNonCommentNode, - rawNodeString, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -export const ruleName = "declaration-block-semicolon-newline-after" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const blockString = require("../../utils/blockString") +const nextNonCommentNode = require("../../utils/nextNonCommentNode") +const rawNodeString = require("../../utils/rawNodeString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") + +const ruleName = "declaration-block-semicolon-newline-after" + +const messages = ruleMessages(ruleName, { expectedAfter: () => "Expected newline after \";\"", expectedAfterMultiLine: () => "Expected newline after \";\" in a multi-line declaration block", rejectedAfterMultiLine: () => "Unexpected newline after \";\" in a multi-line declaration block", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("newline", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { // Ignore last declaration if there's no trailing semicolon const parentRule = decl.parent - if (!parentRule.raws.semicolon && parentRule.last === decl) { return } + if (!parentRule.raws.semicolon && parentRule.last === decl) { + return + } const nextNode = decl.next() - if (!nextNode) { return } + if (!nextNode) { + return + } // Allow end-of-line comment const nodeToCheck = nextNonCommentNode(nextNode) - if (!nodeToCheck) { return } + if (!nodeToCheck) { + return + } checker.afterOneOnly({ source: rawNodeString(nodeToCheck), @@ -59,3 +63,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-semicolon-newline-before/README.md b/lib/rules/declaration-block-semicolon-newline-before/README.md similarity index 100% rename from src/rules/declaration-block-semicolon-newline-before/README.md rename to lib/rules/declaration-block-semicolon-newline-before/README.md diff --git a/src/rules/declaration-block-semicolon-newline-before/__tests__/index.js b/lib/rules/declaration-block-semicolon-newline-before/__tests__/index.js similarity index 95% rename from src/rules/declaration-block-semicolon-newline-before/__tests__/index.js rename to lib/rules/declaration-block-semicolon-newline-before/__tests__/index.js index e4153c7f9e..fb8a31d206 100644 --- a/src/rules/declaration-block-semicolon-newline-before/__tests__/index.js +++ b/lib/rules/declaration-block-semicolon-newline-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/declaration-block-semicolon-newline-before/index.js b/lib/rules/declaration-block-semicolon-newline-before/index.js similarity index 60% rename from src/rules/declaration-block-semicolon-newline-before/index.js rename to lib/rules/declaration-block-semicolon-newline-before/index.js index 597f30bd14..addfb6d39c 100644 --- a/src/rules/declaration-block-semicolon-newline-before/index.js +++ b/lib/rules/declaration-block-semicolon-newline-before/index.js @@ -1,36 +1,36 @@ -import { - blockString, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" +"use strict" -export const ruleName = "declaration-block-semicolon-newline-before" +const blockString = require("../../utils/blockString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "declaration-block-semicolon-newline-before" + +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected newline before \";\"", expectedBeforeMultiLine: () => "Expected newline before \";\" in a multi-line declaration block", rejectedBeforeMultiLine: () => "Unexpected whitespace before \";\" in a multi-line declaration block", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("newline", expectation, messages) return function (root, result) { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(function (decl) { const parentRule = decl.parent - if (!parentRule.raws.semicolon && parentRule.last === decl) { return } + if (!parentRule.raws.semicolon && parentRule.last === decl) { + return + } const declString = decl.toString() @@ -51,3 +51,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-semicolon-space-after/README.md b/lib/rules/declaration-block-semicolon-space-after/README.md similarity index 100% rename from src/rules/declaration-block-semicolon-space-after/README.md rename to lib/rules/declaration-block-semicolon-space-after/README.md diff --git a/src/rules/declaration-block-semicolon-space-after/__tests__/index.js b/lib/rules/declaration-block-semicolon-space-after/__tests__/index.js similarity index 95% rename from src/rules/declaration-block-semicolon-space-after/__tests__/index.js rename to lib/rules/declaration-block-semicolon-space-after/__tests__/index.js index 1ad866d0f7..fedb9a42ab 100644 --- a/src/rules/declaration-block-semicolon-space-after/__tests__/index.js +++ b/lib/rules/declaration-block-semicolon-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/declaration-block-semicolon-space-after/index.js b/lib/rules/declaration-block-semicolon-space-after/index.js similarity index 59% rename from src/rules/declaration-block-semicolon-space-after/index.js rename to lib/rules/declaration-block-semicolon-space-after/index.js index fa090e6e24..f0068b1904 100644 --- a/src/rules/declaration-block-semicolon-space-after/index.js +++ b/lib/rules/declaration-block-semicolon-space-after/index.js @@ -1,43 +1,44 @@ -import { - blockString, - rawNodeString, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -export const ruleName = "declaration-block-semicolon-space-after" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const blockString = require("../../utils/blockString") +const rawNodeString = require("../../utils/rawNodeString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") + +const ruleName = "declaration-block-semicolon-space-after" + +const messages = ruleMessages(ruleName, { expectedAfter: () => "Expected single space after \";\"", rejectedAfter: () => "Unexpected whitespace after \";\"", expectedAfterSingleLine: () => "Expected single space after \";\" in a single-line declaration block", rejectedAfterSingleLine: () => "Unexpected whitespace after \";\" in a single-line declaration block", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return function (root, result) { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(function (decl) { // Ignore last declaration if there's no trailing semicolon const parentRule = decl.parent - if (!parentRule.raws.semicolon && parentRule.last === decl) { return } + if (!parentRule.raws.semicolon && parentRule.last === decl) { + return + } const nextDecl = decl.next() - if (!nextDecl) { return } + if (!nextDecl) { + return + } checker.after({ source: rawNodeString(nextDecl), @@ -56,3 +57,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-semicolon-space-before/README.md b/lib/rules/declaration-block-semicolon-space-before/README.md similarity index 100% rename from src/rules/declaration-block-semicolon-space-before/README.md rename to lib/rules/declaration-block-semicolon-space-before/README.md diff --git a/src/rules/declaration-block-semicolon-space-before/__tests__/index.js b/lib/rules/declaration-block-semicolon-space-before/__tests__/index.js similarity index 96% rename from src/rules/declaration-block-semicolon-space-before/__tests__/index.js rename to lib/rules/declaration-block-semicolon-space-before/__tests__/index.js index 539bc030a4..dacbed7aa1 100644 --- a/src/rules/declaration-block-semicolon-space-before/__tests__/index.js +++ b/lib/rules/declaration-block-semicolon-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/declaration-block-semicolon-space-before/index.js b/lib/rules/declaration-block-semicolon-space-before/index.js similarity index 62% rename from src/rules/declaration-block-semicolon-space-before/index.js rename to lib/rules/declaration-block-semicolon-space-before/index.js index fd6d25eb46..3c82578ddf 100644 --- a/src/rules/declaration-block-semicolon-space-before/index.js +++ b/lib/rules/declaration-block-semicolon-space-before/index.js @@ -1,39 +1,38 @@ -import { - blockString, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" +"use strict" -export const ruleName = "declaration-block-semicolon-space-before" +const blockString = require("../../utils/blockString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "declaration-block-semicolon-space-before" + +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected single space before \";\"", rejectedBefore: () => "Unexpected whitespace before \";\"", expectedBeforeSingleLine: () => "Expected single space before \";\" in a single-line declaration block", rejectedBeforeSingleLine: () => "Unexpected whitespace before \";\" in a single-line declaration block", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { // Ignore last declaration if there's no trailing semicolon const parentRule = decl.parent - if (!parentRule.raws.semicolon && parentRule.last === decl) { return } + if (!parentRule.raws.semicolon && parentRule.last === decl) { + return + } const declString = decl.toString() @@ -54,3 +53,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-single-line-max-declarations/README.md b/lib/rules/declaration-block-single-line-max-declarations/README.md similarity index 100% rename from src/rules/declaration-block-single-line-max-declarations/README.md rename to lib/rules/declaration-block-single-line-max-declarations/README.md diff --git a/src/rules/declaration-block-single-line-max-declarations/__tests__/index.js b/lib/rules/declaration-block-single-line-max-declarations/__tests__/index.js similarity index 89% rename from src/rules/declaration-block-single-line-max-declarations/__tests__/index.js rename to lib/rules/declaration-block-single-line-max-declarations/__tests__/index.js index a0464e1a8f..92c79e8329 100644 --- a/src/rules/declaration-block-single-line-max-declarations/__tests__/index.js +++ b/lib/rules/declaration-block-single-line-max-declarations/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -80,6 +80,5 @@ testRule(rule, { accept: [{ code: "a { .tab-focus(); }", description: "single mixin", - }, - ], + }], }) diff --git a/lib/rules/declaration-block-single-line-max-declarations/index.js b/lib/rules/declaration-block-single-line-max-declarations/index.js new file mode 100644 index 0000000000..20bf7ece86 --- /dev/null +++ b/lib/rules/declaration-block-single-line-max-declarations/index.js @@ -0,0 +1,54 @@ +"use strict" + +const beforeBlockString = require("../../utils/beforeBlockString") +const blockString = require("../../utils/blockString") +const isSingleLineString = require("../../utils/isSingleLineString") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") + +const ruleName = "declaration-block-single-line-max-declarations" + +const messages = ruleMessages(ruleName, { + expected: quantity => `Expected no more than ${quantity} declaration(s)`, +}) + +const rule = function (quantity) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: quantity, + possible: [_.isNumber], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isSingleLineString(blockString(rule))) { + return + } + if (!rule.nodes) { + return + } + + const decls = rule.nodes.filter(node => node.type === "decl") + + if (decls.length <= quantity) { + return + } + + report({ + message: messages.expected(quantity), + node: rule, + index: beforeBlockString(rule, { noRawBefore: true }).length, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-block-trailing-semicolon/README.md b/lib/rules/declaration-block-trailing-semicolon/README.md similarity index 100% rename from src/rules/declaration-block-trailing-semicolon/README.md rename to lib/rules/declaration-block-trailing-semicolon/README.md diff --git a/src/rules/declaration-block-trailing-semicolon/__tests__/index.js b/lib/rules/declaration-block-trailing-semicolon/__tests__/index.js similarity index 94% rename from src/rules/declaration-block-trailing-semicolon/__tests__/index.js rename to lib/rules/declaration-block-trailing-semicolon/__tests__/index.js index ad50dce88d..7f992d0974 100644 --- a/src/rules/declaration-block-trailing-semicolon/__tests__/index.js +++ b/lib/rules/declaration-block-trailing-semicolon/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-block-trailing-semicolon/index.js b/lib/rules/declaration-block-trailing-semicolon/index.js new file mode 100644 index 0000000000..0d41d6e620 --- /dev/null +++ b/lib/rules/declaration-block-trailing-semicolon/index.js @@ -0,0 +1,74 @@ +"use strict" + +const hasBlock = require("../../utils/hasBlock") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "declaration-block-trailing-semicolon" + +const messages = ruleMessages(ruleName, { + expected: "Expected a trailing semicolon", + rejected: "Unexpected trailing semicolon", +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + if (atRule.parent === root) { + return + } + if (atRule !== atRule.parent.last) { + return + } + if (hasBlock(atRule)) { + return + } + checkLastNode(atRule) + }) + + root.walkDecls(decl => { + if (decl !== decl.parent.last) { + return + } + checkLastNode(decl) + }) + + function checkLastNode(node) { + let message + + if (expectation === "always") { + if (node.parent.raws.semicolon) { + return + } + message = messages.expected + } + if (expectation === "never") { + if (!node.parent.raws.semicolon) { + return + } + message = messages.rejected + } + + report({ + message, + node, + index: node.toString().trim().length - 1, + result, + ruleName, + }) + } + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-colon-newline-after/README.md b/lib/rules/declaration-colon-newline-after/README.md similarity index 100% rename from src/rules/declaration-colon-newline-after/README.md rename to lib/rules/declaration-colon-newline-after/README.md diff --git a/src/rules/declaration-colon-newline-after/__tests__/index.js b/lib/rules/declaration-colon-newline-after/__tests__/index.js similarity index 76% rename from src/rules/declaration-colon-newline-after/__tests__/index.js rename to lib/rules/declaration-colon-newline-after/__tests__/index.js index d0fea1f345..a1fa7bf7b8 100644 --- a/src/rules/declaration-colon-newline-after/__tests__/index.js +++ b/lib/rules/declaration-colon-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -12,10 +12,7 @@ testRule(rule, { config: ["always"], accept: [ { - code: "a {\n" + - " color:\n" + - " pink\n" + - "}", + code: "a {\n" + " color:\n" + " pink\n" + "}", description: "newline and spaces after", }, { @@ -73,15 +70,9 @@ testRule(rule, { config: ["always-multi-line"], accept: [ { - code: "a {\n" + - " color: pink\n" + - "}", + code: "a {\n" + " color: pink\n" + "}", }, { - code: "a {\n" + - " box-shadow:\n" + - " 0 0 0 1px #5b9dd9\n" + - " 0 0 2px 1px rgba(30, 140, 190, 0.8);\n" + - "}", + code: "a {\n" + " box-shadow:\n" + " 0 0 0 1px #5b9dd9\n" + " 0 0 2px 1px rgba(30, 140, 190, 0.8);\n" + "}", }, { code: "$map\n: (\nkey: value,\nkey2 :value2)", description: "SCSS map with newlines", @@ -99,19 +90,13 @@ testRule(rule, { } ], reject: [ { - code: "a {\n" + - " box-shadow: 0 0 0 1px #5b9dd9\n" + - " 0 0 2px 1px rgba(30, 140, 190, 0.8);\n" + - "}", + code: "a {\n" + " box-shadow: 0 0 0 1px #5b9dd9\n" + " 0 0 2px 1px rgba(30, 140, 190, 0.8);\n" + "}", message: messages.expectedAfterMultiLine(), line: 2, column: 13, }, { - code: "a {\n" + - " box-shadow:0 0 0 1px #5b9dd9\n" + - " 0 0 2px 1px rgba(30, 140, 190, 0.8);\n" + - "}", + code: "a {\n" + " box-shadow:0 0 0 1px #5b9dd9\n" + " 0 0 2px 1px rgba(30, 140, 190, 0.8);\n" + "}", message: messages.expectedAfterMultiLine(), line: 2, diff --git a/src/rules/declaration-colon-newline-after/index.js b/lib/rules/declaration-colon-newline-after/index.js similarity index 55% rename from src/rules/declaration-colon-newline-after/index.js rename to lib/rules/declaration-colon-newline-after/index.js index 56b94e1e91..6b95d1eac6 100644 --- a/src/rules/declaration-colon-newline-after/index.js +++ b/lib/rules/declaration-colon-newline-after/index.js @@ -1,33 +1,34 @@ -import { - declarationValueIndex, - isStandardSyntaxDeclaration, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -export const ruleName = "declaration-colon-newline-after" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") + +const ruleName = "declaration-colon-newline-after" + +const messages = ruleMessages(ruleName, { expectedAfter: () => "Expected newline after \":\"", expectedAfterMultiLine: () => "Expected newline after \":\" with a multi-line declaration", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("newline", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-multi-line", - ], + possible: [ "always", "always-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { - if (!isStandardSyntaxDeclaration(decl)) { return } + if (!isStandardSyntaxDeclaration(decl)) { + return + } // Get the raw prop, and only the prop const endOfPropIndex = declarationValueIndex(decl) + decl.raws.between.length - 1 @@ -37,10 +38,10 @@ export default function (expectation) { const propPlusColon = decl.toString().slice(0, endOfPropIndex) + "xxx" for (let i = 0, l = propPlusColon.length; i < l; i++) { - if (propPlusColon[i] !== ":") { continue } - const indexToCheck = (propPlusColon.substr(propPlusColon[i], 3) === "/*") - ? propPlusColon.indexOf("*/", i) + 1 - : i + if (propPlusColon[i] !== ":") { + continue + } + const indexToCheck = propPlusColon.substr(propPlusColon[i], 3) === "/*" ? propPlusColon.indexOf("*/", i) + 1 : i checker.afterOneOnly({ source: propPlusColon, @@ -60,3 +61,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-colon-space-after/README.md b/lib/rules/declaration-colon-space-after/README.md similarity index 100% rename from src/rules/declaration-colon-space-after/README.md rename to lib/rules/declaration-colon-space-after/README.md diff --git a/src/rules/declaration-colon-space-after/__tests__/index.js b/lib/rules/declaration-colon-space-after/__tests__/index.js similarity index 95% rename from src/rules/declaration-colon-space-after/__tests__/index.js rename to lib/rules/declaration-colon-space-after/__tests__/index.js index a8fbd9965e..0cdefa91f9 100644 --- a/src/rules/declaration-colon-space-after/__tests__/index.js +++ b/lib/rules/declaration-colon-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-colon-space-after/index.js b/lib/rules/declaration-colon-space-after/index.js new file mode 100644 index 0000000000..c4fe25627d --- /dev/null +++ b/lib/rules/declaration-colon-space-after/index.js @@ -0,0 +1,38 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const declarationColonSpaceChecker = require("../declarationColonSpaceChecker") + +const ruleName = "declaration-colon-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected single space after \":\"", + rejectedAfter: () => "Unexpected whitespace after \":\"", + expectedAfterSingleLine: () => "Expected single space after \":\" with a single-line declaration", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never", "always-single-line" ], + }) + if (!validOptions) { + return + } + + declarationColonSpaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-colon-space-before/README.md b/lib/rules/declaration-colon-space-before/README.md similarity index 100% rename from src/rules/declaration-colon-space-before/README.md rename to lib/rules/declaration-colon-space-before/README.md diff --git a/src/rules/declaration-colon-space-before/__tests__/index.js b/lib/rules/declaration-colon-space-before/__tests__/index.js similarity index 93% rename from src/rules/declaration-colon-space-before/__tests__/index.js rename to lib/rules/declaration-colon-space-before/__tests__/index.js index eed7742cc9..abf23b3640 100644 --- a/src/rules/declaration-colon-space-before/__tests__/index.js +++ b/lib/rules/declaration-colon-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-colon-space-before/index.js b/lib/rules/declaration-colon-space-before/index.js new file mode 100644 index 0000000000..fbc3a88f93 --- /dev/null +++ b/lib/rules/declaration-colon-space-before/index.js @@ -0,0 +1,37 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const declarationColonSpaceChecker = require("../declarationColonSpaceChecker") + +const ruleName = "declaration-colon-space-before" + +const messages = ruleMessages(ruleName, { + expectedBefore: () => "Expected single space before \":\"", + rejectedBefore: () => "Unexpected whitespace before \":\"", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + declarationColonSpaceChecker({ + root, + result, + locationChecker: checker.before, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-empty-line-before/README.md b/lib/rules/declaration-empty-line-before/README.md similarity index 100% rename from src/rules/declaration-empty-line-before/README.md rename to lib/rules/declaration-empty-line-before/README.md diff --git a/src/rules/declaration-empty-line-before/__tests__/index.js b/lib/rules/declaration-empty-line-before/__tests__/index.js similarity index 97% rename from src/rules/declaration-empty-line-before/__tests__/index.js rename to lib/rules/declaration-empty-line-before/__tests__/index.js index cc262e50f7..f6b9856bee 100644 --- a/src/rules/declaration-empty-line-before/__tests__/index.js +++ b/lib/rules/declaration-empty-line-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -366,11 +366,7 @@ testRule(rule, { testRule(rule, { ruleName, config: [ "always", { - except: [ - "first-nested", - "after-comment", - "after-declaration", - ] } ], + except: [ "first-nested", "after-comment", "after-declaration" ] } ], accept: [{ code: "a {\n top: 15px; \n bottom: 5px; \n /* comment */ \n prop: 15px;\n\n @extends 'x';\n\n prop: 15px; \n & b {\n prop: 15px;\n } \n\n prop: 15px; \n }" }], }) @@ -525,11 +521,7 @@ testRule(rule, { testRule(rule, { ruleName, config: [ "never", { - except: [ - "first-nested", - "after-comment", - "after-declaration", - ] } ], + except: [ "first-nested", "after-comment", "after-declaration" ] } ], accept: [{ code: "a {\n\n top: 15px; \n\n bottom: 5px; \n /* comment */ \n\n prop: 15px;\n\n @extends 'x';\n prop: 15px; \n & b {\n\n prop: 15px;\n } \n prop: 15px; \n }" }], diff --git a/lib/rules/declaration-empty-line-before/index.js b/lib/rules/declaration-empty-line-before/index.js new file mode 100644 index 0000000000..69e67f29ad --- /dev/null +++ b/lib/rules/declaration-empty-line-before/index.js @@ -0,0 +1,101 @@ +"use strict" + +const blockString = require("../../utils/blockString") +const hasEmptyLine = require("../../utils/hasEmptyLine") +const isCustomProperty = require("../../utils/isCustomProperty") +const isSingleLineString = require("../../utils/isSingleLineString") +const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "declaration-empty-line-before" + +const messages = ruleMessages(ruleName, { + expected: "Expected empty line before declaration", + rejected: "Unexpected empty line before declaration", +}) + +const rule = function (expectation, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }, { + actual: options, + possible: { + except: [ "first-nested", "after-comment", "after-declaration" ], + ignore: [ "after-comment", "after-declaration", "inside-single-line-block" ], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop, + parent = decl.parent + + if (!isStandardSyntaxDeclaration(decl)) { + return + } + if (isCustomProperty(prop)) { + return + } + + // Optionally ignore the node if a comment precedes it + if (optionsMatches(options, "ignore", "after-comment") && decl.prev() && decl.prev().type === "comment") { + return + } + + // Optionally ignore the node if a declaration precedes it + if (optionsMatches(options, "ignore", "after-declaration") && decl.prev() && decl.prev().type === "decl") { + return + } + + // Optionally ignore nodes inside single-line blocks + if (optionsMatches(options, "ignore", "inside-single-line-block") && isSingleLineString(blockString(parent))) { + return + } + + let expectEmptyLineBefore = expectation === "always" ? true : false + + // Optionally reverse the expectation for the first nested node + if (optionsMatches(options, "except", "first-nested") && decl === parent.first) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Optionally reverse the expectation if a comment precedes this node + if (optionsMatches(options, "except", "after-comment") && decl.prev() && decl.prev().type === "comment") { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Optionally reverse the expectation if a declaration precedes this node + if (optionsMatches(options, "except", "after-declaration") && decl.prev() && decl.prev().prop && isStandardSyntaxDeclaration(decl.prev()) && !isCustomProperty(decl.prev().prop)) { + expectEmptyLineBefore = !expectEmptyLineBefore + } + + // Check for at least one empty line + const hasEmptyLineBefore = hasEmptyLine(decl.raws["before"]) + + // Return if the expectation is met + if (expectEmptyLineBefore === hasEmptyLineBefore) { + return + } + + const message = expectEmptyLineBefore ? messages.expected : messages.rejected + report({ + message, + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-no-important/README.md b/lib/rules/declaration-no-important/README.md similarity index 100% rename from src/rules/declaration-no-important/README.md rename to lib/rules/declaration-no-important/README.md diff --git a/src/rules/declaration-no-important/__tests__/index.js b/lib/rules/declaration-no-important/__tests__/index.js similarity index 76% rename from src/rules/declaration-no-important/__tests__/index.js rename to lib/rules/declaration-no-important/__tests__/index.js index dce1509a75..98cb4d3ec8 100644 --- a/src/rules/declaration-no-important/__tests__/index.js +++ b/lib/rules/declaration-no-important/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-no-important/index.js b/lib/rules/declaration-no-important/index.js new file mode 100644 index 0000000000..e78dde0e54 --- /dev/null +++ b/lib/rules/declaration-no-important/index.js @@ -0,0 +1,38 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "declaration-no-important" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected !important", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + if (!decl.important) { + return + } + + report({ + message: messages.rejected, + node: decl, + word: "important", + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-property-unit-blacklist/README.md b/lib/rules/declaration-property-unit-blacklist/README.md similarity index 100% rename from src/rules/declaration-property-unit-blacklist/README.md rename to lib/rules/declaration-property-unit-blacklist/README.md diff --git a/src/rules/declaration-property-unit-blacklist/__tests__/index.js b/lib/rules/declaration-property-unit-blacklist/__tests__/index.js similarity index 94% rename from src/rules/declaration-property-unit-blacklist/__tests__/index.js rename to lib/rules/declaration-property-unit-blacklist/__tests__/index.js index d5312bf278..640b47db44 100644 --- a/src/rules/declaration-property-unit-blacklist/__tests__/index.js +++ b/lib/rules/declaration-property-unit-blacklist/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-property-unit-blacklist/index.js b/lib/rules/declaration-property-unit-blacklist/index.js new file mode 100644 index 0000000000..0b51da5bde --- /dev/null +++ b/lib/rules/declaration-property-unit-blacklist/index.js @@ -0,0 +1,70 @@ +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const getUnitFromValueNode = require("../../utils/getUnitFromValueNode") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const valueParser = require("postcss-value-parser") +const postcss = require("postcss") + +const ruleName = "declaration-property-unit-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: (property, unit) => `Unexpected unit "${unit}" for property "${property}"`, +}) + +const rule = function (blacklist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isObject], + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop, + value = decl.value + + const unprefixedProp = postcss.vendor.unprefixed(prop) + + const propBlacklist = _.find(blacklist, (list, propIdentifier) => matchesStringOrRegExp(unprefixedProp, propIdentifier)) + + if (!propBlacklist) { + return + } + + valueParser(value).walk(function (node) { + // Ignore wrong units within `url` function + if (node.type === "function" && node.value.toLowerCase() === "url") { + return false + } + if (node.type === "string") { + return + } + + const unit = getUnitFromValueNode(node) + + if (!unit || unit && propBlacklist.indexOf(unit.toLowerCase()) === -1) { + return + } + + report({ + message: messages.rejected(prop, unit), + node: decl, + index: declarationValueIndex(decl) + node.sourceIndex, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-property-unit-whitelist/README.md b/lib/rules/declaration-property-unit-whitelist/README.md similarity index 100% rename from src/rules/declaration-property-unit-whitelist/README.md rename to lib/rules/declaration-property-unit-whitelist/README.md diff --git a/src/rules/declaration-property-unit-whitelist/__tests__/index.js b/lib/rules/declaration-property-unit-whitelist/__tests__/index.js similarity index 94% rename from src/rules/declaration-property-unit-whitelist/__tests__/index.js rename to lib/rules/declaration-property-unit-whitelist/__tests__/index.js index 3e54337297..ca4c502391 100644 --- a/src/rules/declaration-property-unit-whitelist/__tests__/index.js +++ b/lib/rules/declaration-property-unit-whitelist/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/declaration-property-unit-whitelist/index.js b/lib/rules/declaration-property-unit-whitelist/index.js new file mode 100644 index 0000000000..0f559eae9a --- /dev/null +++ b/lib/rules/declaration-property-unit-whitelist/index.js @@ -0,0 +1,70 @@ +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const getUnitFromValueNode = require("../../utils/getUnitFromValueNode") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const valueParser = require("postcss-value-parser") +const postcss = require("postcss") + +const ruleName = "declaration-property-unit-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: (property, unit) => `Unexpected unit "${unit}" for property "${property}"`, +}) + +const rule = function (whitelist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isObject], + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop, + value = decl.value + + const unprefixedProp = postcss.vendor.unprefixed(prop) + + const propWhitelist = _.find(whitelist, (list, propIdentifier) => matchesStringOrRegExp(unprefixedProp, propIdentifier)) + + if (!propWhitelist) { + return + } + + valueParser(value).walk(function (node) { + // Ignore wrong units within `url` function + if (node.type === "function" && node.value.toLowerCase() === "url") { + return false + } + if (node.type === "string") { + return + } + + const unit = getUnitFromValueNode(node) + + if (!unit || (unit && propWhitelist.indexOf(unit.toLowerCase())) !== -1) { + return + } + + report({ + message: messages.rejected(prop, unit), + node: decl, + index: declarationValueIndex(decl) + node.sourceIndex, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-property-value-blacklist/README.md b/lib/rules/declaration-property-value-blacklist/README.md similarity index 100% rename from src/rules/declaration-property-value-blacklist/README.md rename to lib/rules/declaration-property-value-blacklist/README.md diff --git a/src/rules/declaration-property-value-blacklist/__tests__/index.js b/lib/rules/declaration-property-value-blacklist/__tests__/index.js similarity index 91% rename from src/rules/declaration-property-value-blacklist/__tests__/index.js rename to lib/rules/declaration-property-value-blacklist/__tests__/index.js index 3fab1b5b2d..fa3e913c78 100644 --- a/src/rules/declaration-property-value-blacklist/__tests__/index.js +++ b/lib/rules/declaration-property-value-blacklist/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -119,10 +119,8 @@ testRule(rule, { ruleName, config: { position: ["fixed"] }, skipBasicChecks: true, - accept: [ - { - code: "a { font-size: 1em; }", - description: "irrelevant CSS", - }, - ], + accept: [{ + code: "a { font-size: 1em; }", + description: "irrelevant CSS", + }], }) diff --git a/lib/rules/declaration-property-value-blacklist/index.js b/lib/rules/declaration-property-value-blacklist/index.js new file mode 100644 index 0000000000..38fc54336d --- /dev/null +++ b/lib/rules/declaration-property-value-blacklist/index.js @@ -0,0 +1,53 @@ +"use strict" + +const _ = require("lodash") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const postcss = require("postcss") + +const ruleName = "declaration-property-value-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: (property, value) => `Unexpected value "${value}" for property "${property}"`, +}) + +const rule = function (blacklist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isObject], + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop, + value = decl.value + + const unprefixedProp = postcss.vendor.unprefixed(prop) + const propBlacklist = _.find(blacklist, (list, propIdentifier) => matchesStringOrRegExp(unprefixedProp, propIdentifier)) + + if (_.isEmpty(propBlacklist)) { + return + } + + if (!matchesStringOrRegExp(value, propBlacklist)) { + return + } + + report({ + message: messages.rejected(prop, value), + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/declaration-property-value-whitelist/README.md b/lib/rules/declaration-property-value-whitelist/README.md similarity index 100% rename from src/rules/declaration-property-value-whitelist/README.md rename to lib/rules/declaration-property-value-whitelist/README.md diff --git a/src/rules/declaration-property-value-whitelist/__tests__/index.js b/lib/rules/declaration-property-value-whitelist/__tests__/index.js similarity index 81% rename from src/rules/declaration-property-value-whitelist/__tests__/index.js rename to lib/rules/declaration-property-value-whitelist/__tests__/index.js index b8e3bd3e93..fc9f4c4c95 100644 --- a/src/rules/declaration-property-value-whitelist/__tests__/index.js +++ b/lib/rules/declaration-property-value-whitelist/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -60,10 +60,8 @@ testRule(rule, { ruleName, config: { position: ["static"] }, skipBasicChecks: true, - accept: [ - { - code: "a { font-size: 1em; }", - description: "irrelevant CSS", - }, - ], + accept: [{ + code: "a { font-size: 1em; }", + description: "irrelevant CSS", + }], }) diff --git a/lib/rules/declaration-property-value-whitelist/index.js b/lib/rules/declaration-property-value-whitelist/index.js new file mode 100644 index 0000000000..3017e03f8e --- /dev/null +++ b/lib/rules/declaration-property-value-whitelist/index.js @@ -0,0 +1,53 @@ +"use strict" + +const _ = require("lodash") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const postcss = require("postcss") + +const ruleName = "declaration-property-value-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: (property, value) => `Unexpected value "${value}" for property "${property}"`, +}) + +const rule = function (whitelist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isObject], + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop, + value = decl.value + + const unprefixedProp = postcss.vendor.unprefixed(prop) + const propWhitelist = _.find(whitelist, (list, propIdentifier) => matchesStringOrRegExp(unprefixedProp, propIdentifier)) + + if (_.isEmpty(propWhitelist)) { + return + } + + if (matchesStringOrRegExp(value, propWhitelist)) { + return + } + + report({ + message: messages.rejected(prop, value), + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/lib/rules/declarationBangSpaceChecker.js b/lib/rules/declarationBangSpaceChecker.js new file mode 100644 index 0000000000..1e7cf7143a --- /dev/null +++ b/lib/rules/declarationBangSpaceChecker.js @@ -0,0 +1,31 @@ +"use strict" + +const declarationValueIndex = require("../utils/declarationValueIndex") +const report = require("../utils/report") +const styleSearch = require("style-search") + +module.exports = function (opts) { + opts.root.walkDecls(function (decl) { + const indexOffset = declarationValueIndex(decl) + const declString = decl.toString() + const valueString = decl.toString().slice(indexOffset) + if (valueString.indexOf("!") == -1) { + return + } + + styleSearch({ source: valueString, target: "!" }, match => { + check(declString, match.startIndex + indexOffset, decl) + }) + }) + + function check(source, index, node) { + opts.locationChecker({ source, index, err: m => report({ + message: m, + node, + index, + result: opts.result, + ruleName: opts.checkedRuleName, + }), + }) + } +} diff --git a/lib/rules/declarationColonSpaceChecker.js b/lib/rules/declarationColonSpaceChecker.js new file mode 100644 index 0000000000..6baab182d1 --- /dev/null +++ b/lib/rules/declarationColonSpaceChecker.js @@ -0,0 +1,41 @@ +"use strict" + +const declarationValueIndex = require("../utils/declarationValueIndex") +const isStandardSyntaxDeclaration = require("../utils/isStandardSyntaxDeclaration") +const report = require("../utils/report") + +module.exports = function (opts) { + opts.root.walkDecls(decl => { + if (!isStandardSyntaxDeclaration(decl)) { + return + } + + // Get the raw prop, and only the prop + const endOfPropIndex = declarationValueIndex(decl) + decl.raws.between.length - 1 + + // The extra characters tacked onto the end ensure that there is a character to check + // after the colon. Otherwise, with `background:pink` the character after the + const propPlusColon = decl.toString().slice(0, endOfPropIndex) + "xxx" + + for (let i = 0, l = propPlusColon.length; i < l; i++) { + if (propPlusColon[i] !== ":") { + continue + } + opts.locationChecker({ + source: propPlusColon, + index: i, + lineCheckStr: decl.value, + err: m => { + report({ + message: m, + node: decl, + index: decl.prop.toString().length + 1, + result: opts.result, + ruleName: opts.checkedRuleName, + }) + }, + }) + break + } + }) +} diff --git a/lib/rules/findMediaOperator.js b/lib/rules/findMediaOperator.js new file mode 100644 index 0000000000..0058dfb8b7 --- /dev/null +++ b/lib/rules/findMediaOperator.js @@ -0,0 +1,15 @@ +"use strict" + +const rangeOperatorRegex = /[^><](>=?|<=?|=)/g + +module.exports = function (atRule, cb) { + if (atRule.name.toLowerCase() !== "media") { + return + } + + const params = atRule.params + let match + while ((match = rangeOperatorRegex.exec(params)) !== null) { + cb(match, params, atRule) + } +} diff --git a/src/rules/font-family-name-quotes/README.md b/lib/rules/font-family-name-quotes/README.md similarity index 100% rename from src/rules/font-family-name-quotes/README.md rename to lib/rules/font-family-name-quotes/README.md diff --git a/src/rules/font-family-name-quotes/__tests__/index.js b/lib/rules/font-family-name-quotes/__tests__/index.js similarity index 91% rename from src/rules/font-family-name-quotes/__tests__/index.js rename to lib/rules/font-family-name-quotes/__tests__/index.js index 12729fe115..72e551dd92 100644 --- a/src/rules/font-family-name-quotes/__tests__/index.js +++ b/lib/rules/font-family-name-quotes/__tests__/index.js @@ -1,30 +1,28 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] -const variablePositiveTests = [ - { - code: "a { font-family: $sassy-font-family; }", - description: "ignores Sass variables", - }, { - code: "a { font-family: @less-666; }", - description: "ignores Less variables", - }, { - code: "a { font-family: var(--ff1); }", - description: "ignores custom properties", - }, { - code: "$font-family: map-get($font-stacks, default); $default-font-family: pink", - description: "ignores Sass variable containing font-family in name", - }, { - code: "@font-family: map-get(@font-stacks, default); @default-font-family: pink", - description: "ignores Less variable containing font-family in name", - }, -] +const variablePositiveTests = [ { + code: "a { font-family: $sassy-font-family; }", + description: "ignores Sass variables", +}, { + code: "a { font-family: @less-666; }", + description: "ignores Less variables", +}, { + code: "a { font-family: var(--ff1); }", + description: "ignores custom properties", +}, { + code: "$font-family: map-get($font-stacks, default); $default-font-family: pink", + description: "ignores Sass variable containing font-family in name", +}, { + code: "@font-family: map-get(@font-stacks, default); @default-font-family: pink", + description: "ignores Less variable containing font-family in name", +} ] testRule(rule, { ruleName, diff --git a/src/rules/font-family-name-quotes/index.js b/lib/rules/font-family-name-quotes/index.js similarity index 65% rename from src/rules/font-family-name-quotes/index.js rename to lib/rules/font-family-name-quotes/index.js index eecb9f100c..f36afb6704 100644 --- a/src/rules/font-family-name-quotes/index.js +++ b/lib/rules/font-family-name-quotes/index.js @@ -1,23 +1,27 @@ -import { - findFontFamily, - isStandardSyntaxValue, - isVariable, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { fontFamilyKeywords } from "../../reference/keywordSets" - -export const ruleName = "font-family-name-quotes" - -export const messages = ruleMessages(ruleName, { - expected: (family) => `Expected quotes around "${family}"`, - rejected: (family) => `Unexpected quotes around "${family}"`, +"use strict" + +const findFontFamily = require("../../utils/findFontFamily") +const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue") +const isVariable = require("../../utils/isVariable") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") + +const ruleName = "font-family-name-quotes" + +const messages = ruleMessages(ruleName, { + expected: family => `Expected quotes around "${family}"`, + rejected: family => `Unexpected quotes around "${family}"`, }) function isSystemFontKeyword(font) { - if (font.indexOf("-apple-") === 0) { return true } - if (font === "BlinkMacSystemFont") { return true } + if (font.indexOf("-apple-") === 0) { + return true + } + if (font === "BlinkMacSystemFont") { + return true + } return false } @@ -32,29 +36,27 @@ function quotesRecommended(family) { // (regexes from https://mathiasbynens.be/notes/unquoted-font-family) function quotesRequired(family) { return family.split(/\s+/).some(word => { - return ( - /^(-?\d|--)/.test(word) - || !/^[-_a-zA-Z0-9\u00A0-\u10FFFF]+$/.test(word) + return (/^(-?\d|--)/.test(word) || !/^[-_a-zA-Z0-9\u00A0-\u10FFFF]+$/.test(word) ) }) } -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always-where-required", - "always-where-recommended", - "always-unless-keyword", - ], + possible: [ "always-where-required", "always-where-recommended", "always-unless-keyword" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(/^font(-family)?$/i, decl => { const fontFamilies = findFontFamily(decl.value) - if (fontFamilies.length === 0) { return } + if (fontFamilies.length === 0) { + return + } fontFamilies.forEach(fontFamilyNode => { let rawFamily = fontFamilyNode.value @@ -68,8 +70,12 @@ export default function (expectation) { }) function checkFamilyName(rawFamily, decl) { - if (!isStandardSyntaxValue(rawFamily)) { return } - if (isVariable(rawFamily)) { return } + if (!isStandardSyntaxValue(rawFamily)) { + return + } + if (isVariable(rawFamily)) { + return + } const hasQuotes = rawFamily[0] === "'" || rawFamily[0] === "\"" @@ -78,7 +84,7 @@ export default function (expectation) { // Disallow quotes around (case-insensitive) keywords // and system font keywords in all cases - if (fontFamilyKeywords.has(family.toLowerCase()) || isSystemFontKeyword(family)) { + if (keywordSets.fontFamilyKeywords.has(family.toLowerCase()) || isSystemFontKeyword(family)) { if (hasQuotes) { return complain(messages.rejected(family), family, decl) } @@ -126,3 +132,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/font-family-no-duplicate-names/README.md b/lib/rules/font-family-no-duplicate-names/README.md similarity index 100% rename from src/rules/font-family-no-duplicate-names/README.md rename to lib/rules/font-family-no-duplicate-names/README.md diff --git a/src/rules/font-family-no-duplicate-names/__tests__/index.js b/lib/rules/font-family-no-duplicate-names/__tests__/index.js similarity index 88% rename from src/rules/font-family-no-duplicate-names/__tests__/index.js rename to lib/rules/font-family-no-duplicate-names/__tests__/index.js index 1b89bc5a7b..53d4b03bfe 100644 --- a/src/rules/font-family-no-duplicate-names/__tests__/index.js +++ b/lib/rules/font-family-no-duplicate-names/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/font-family-no-duplicate-names/index.js b/lib/rules/font-family-no-duplicate-names/index.js similarity index 57% rename from src/rules/font-family-no-duplicate-names/index.js rename to lib/rules/font-family-no-duplicate-names/index.js index 2e04f9e945..c9e5f9394f 100644 --- a/src/rules/font-family-no-duplicate-names/index.js +++ b/lib/rules/font-family-no-duplicate-names/index.js @@ -1,24 +1,26 @@ -import { - declarationValueIndex, - findFontFamily, - report, - ruleMessages, - validateOptions, - } from "../../utils" -import { fontFamilyKeywords } from "../../reference/keywordSets" - -export const ruleName = "font-family-no-duplicate-names" - -export const messages = ruleMessages(ruleName, { - rejected: (name) => `Unexpected duplicate name ${name}`, +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const findFontFamily = require("../../utils/findFontFamily") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") + +const ruleName = "font-family-no-duplicate-names" + +const messages = ruleMessages(ruleName, { + rejected: name => `Unexpected duplicate name ${name}`, }) -const isFamilyNameKeyword = (node) => !node.quote && fontFamilyKeywords.has(node.value.toLowerCase()) +const isFamilyNameKeyword = node => !node.quote && keywordSets.fontFamilyKeywords.has(node.value.toLowerCase()) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(/^font(-family)?$/i, decl => { const keywords = new Set() @@ -26,7 +28,9 @@ export default function (actual) { const fontFamilies = findFontFamily(decl.value) - if (fontFamilies.length === 0) { return } + if (fontFamilies.length === 0) { + return + } fontFamilies.forEach(fontFamilyNode => { if (isFamilyNameKeyword(fontFamilyNode)) { @@ -64,3 +68,6 @@ export default function (actual) { } } +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/font-weight-notation/README.md b/lib/rules/font-weight-notation/README.md similarity index 100% rename from src/rules/font-weight-notation/README.md rename to lib/rules/font-weight-notation/README.md diff --git a/src/rules/font-weight-notation/__tests__/index.js b/lib/rules/font-weight-notation/__tests__/index.js similarity index 97% rename from src/rules/font-weight-notation/__tests__/index.js rename to lib/rules/font-weight-notation/__tests__/index.js index b3440c65ce..64bb360579 100644 --- a/src/rules/font-weight-notation/__tests__/index.js +++ b/lib/rules/font-weight-notation/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/font-weight-notation/index.js b/lib/rules/font-weight-notation/index.js similarity index 55% rename from src/rules/font-weight-notation/index.js rename to lib/rules/font-weight-notation/index.js index f8aee42007..aeecb05542 100644 --- a/src/rules/font-weight-notation/index.js +++ b/lib/rules/font-weight-notation/index.js @@ -1,23 +1,20 @@ -import { - declarationValueIndex, - isNumbery, - isStandardSyntaxValue, - isVariable, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - fontWeightKeywords, - fontWeightRelativeKeywords, -} from "../../reference/keywordSets" -import { includes } from "lodash" -import postcss from "postcss" - -export const ruleName = "font-weight-notation" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const isNumbery = require("../../utils/isNumbery") +const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue") +const isVariable = require("../../utils/isVariable") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "font-weight-notation" + +const messages = ruleMessages(ruleName, { expected: type => `Expected ${type} font-weight notation`, invalidNamed: name => `Unexpected invalid font-weight name "${name}"`, }) @@ -27,7 +24,7 @@ const INITIAL_KEYWORD = "initial" const NORMAL_KEYWORD = "normal" const WEIGHTS_WITH_KEYWORD_EQUIVALENTS = [ "400", "700" ] -export default function (expectation, options) { +const rule = function (expectation, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, @@ -39,7 +36,9 @@ export default function (expectation, options) { }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { if (decl.prop.toLowerCase() === "font-weight") { @@ -59,11 +58,7 @@ export default function (expectation, options) { const hasNumericFontWeight = valueList.some(isNumbery) for (const value of postcss.list.space(decl.value)) { - if ( - (value.toLowerCase() === NORMAL_KEYWORD && !hasNumericFontWeight) - || isNumbery(value) - || value.toLowerCase() !== NORMAL_KEYWORD && fontWeightKeywords.has(value.toLowerCase()) - ) { + if (value.toLowerCase() === NORMAL_KEYWORD && !hasNumericFontWeight || isNumbery(value) || value.toLowerCase() !== NORMAL_KEYWORD && keywordSets.fontWeightKeywords.has(value.toLowerCase())) { checkWeight(value, decl) return } @@ -71,14 +66,19 @@ export default function (expectation, options) { } function checkWeight(weightValue, decl) { - if (!isStandardSyntaxValue(weightValue)) { return } - if (isVariable(weightValue)) { return } - if (weightValue.toLowerCase() === INHERIT_KEYWORD - || weightValue.toLowerCase() === INITIAL_KEYWORD - ) { return } + if (!isStandardSyntaxValue(weightValue)) { + return + } + if (isVariable(weightValue)) { + return + } + if (weightValue.toLowerCase() === INHERIT_KEYWORD || weightValue.toLowerCase() === INITIAL_KEYWORD) { + return + } - if (optionsMatches(options, "ignore", "relative") && - fontWeightRelativeKeywords.has(weightValue.toLowerCase())) { return } + if (optionsMatches(options, "ignore", "relative") && keywordSets.fontWeightRelativeKeywords.has(weightValue.toLowerCase())) { + return + } const weightValueOffset = decl.value.indexOf(weightValue) @@ -90,14 +90,12 @@ export default function (expectation, options) { if (expectation === "named-where-possible") { if (isNumbery(weightValue)) { - if (includes(WEIGHTS_WITH_KEYWORD_EQUIVALENTS, weightValue)) { + if (_.includes(WEIGHTS_WITH_KEYWORD_EQUIVALENTS, weightValue)) { complain(messages.expected("named")) } return } - if (!fontWeightKeywords.has(weightValue.toLowerCase()) - && weightValue.toLowerCase() !== NORMAL_KEYWORD - ) { + if (!keywordSets.fontWeightKeywords.has(weightValue.toLowerCase()) && weightValue.toLowerCase() !== NORMAL_KEYWORD) { return complain(messages.invalidNamed(weightValue)) } return @@ -115,3 +113,7 @@ export default function (expectation, options) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-blacklist/README.md b/lib/rules/function-blacklist/README.md similarity index 100% rename from src/rules/function-blacklist/README.md rename to lib/rules/function-blacklist/README.md diff --git a/src/rules/function-blacklist/__tests__/index.js b/lib/rules/function-blacklist/__tests__/index.js similarity index 90% rename from src/rules/function-blacklist/__tests__/index.js rename to lib/rules/function-blacklist/__tests__/index.js index 406b96075b..954a6a27fe 100644 --- a/src/rules/function-blacklist/__tests__/index.js +++ b/lib/rules/function-blacklist/__tests__/index.js @@ -1,20 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "rgba", - "scale", - "linear-gradient", - ]], + config: [[ "rgba", "scale", "linear-gradient" ]], accept: [ { code: "a { color: pink; }", diff --git a/lib/rules/function-blacklist/index.js b/lib/rules/function-blacklist/index.js new file mode 100644 index 0000000000..e6e5a0d585 --- /dev/null +++ b/lib/rules/function-blacklist/index.js @@ -0,0 +1,58 @@ +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const isStandardSyntaxFunction = require("../../utils/isStandardSyntaxFunction") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const valueParser = require("postcss-value-parser") +const postcss = require("postcss") + +const ruleName = "function-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: name => `Unexpected function "${name}"`, +}) + +const rule = function (blacklist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + root.walkDecls(decl => { + const value = decl.value + + valueParser(value).walk(function (node) { + if (node.type !== "function") { + return + } + if (!isStandardSyntaxFunction(node)) { + return + } + if (!matchesStringOrRegExp(postcss.vendor.unprefixed(node.value).toLowerCase(), blacklist)) { + return + } + + report({ + message: messages.rejected(node.value), + node: decl, + index: declarationValueIndex(decl) + node.sourceIndex, + result, + ruleName, + }) + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-calc-no-unspaced-operator/README.md b/lib/rules/function-calc-no-unspaced-operator/README.md similarity index 100% rename from src/rules/function-calc-no-unspaced-operator/README.md rename to lib/rules/function-calc-no-unspaced-operator/README.md diff --git a/src/rules/function-calc-no-unspaced-operator/__tests__/index.js b/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js similarity index 97% rename from src/rules/function-calc-no-unspaced-operator/__tests__/index.js rename to lib/rules/function-calc-no-unspaced-operator/__tests__/index.js index 1f9186d68a..ffeb2741f5 100644 --- a/src/rules/function-calc-no-unspaced-operator/__tests__/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/function-calc-no-unspaced-operator/index.js b/lib/rules/function-calc-no-unspaced-operator/index.js similarity index 66% rename from src/rules/function-calc-no-unspaced-operator/index.js rename to lib/rules/function-calc-no-unspaced-operator/index.js index 2f5bc46162..4f19773666 100644 --- a/src/rules/function-calc-no-unspaced-operator/index.js +++ b/lib/rules/function-calc-no-unspaced-operator/index.js @@ -1,25 +1,27 @@ -import { - isWhitespace, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import balancedMatch from "balanced-match" -import styleSearch from "style-search" -import valueParser from "postcss-value-parser" - -export const ruleName = "function-calc-no-unspaced-operator" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const isWhitespace = require("../../utils/isWhitespace") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const balancedMatch = require("balanced-match") +const styleSearch = require("style-search") +const valueParser = require("postcss-value-parser") + +const ruleName = "function-calc-no-unspaced-operator" + +const messages = ruleMessages(ruleName, { expectedBefore: operator => `Expected single space before "${operator}" operator`, expectedAfter: operator => `Expected single space after "${operator}" operator`, expectedOperatorBeforeSign: operator => `Expected an operator before sign "${operator}"`, }) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } function complain(message, node, index) { report({ message, node, index, result, ruleName }) @@ -27,14 +29,13 @@ export default function (actual) { root.walkDecls(decl => { valueParser(decl.value).walk(node => { - if (node.type !== "function" || node.value.toLowerCase() !== "calc") { return } + if (node.type !== "function" || node.value.toLowerCase() !== "calc") { + return + } const parensMatch = balancedMatch("(", ")", valueParser.stringify(node)) const rawExpression = parensMatch.body - const expressionIndex = decl.source.start.column - + decl.prop.length - + decl.raws.between.length - + node.sourceIndex + const expressionIndex = decl.source.start.column + decl.prop.length + decl.raws.between.length + node.sourceIndex const expression = blurVariables(rawExpression) checkSymbol("+") @@ -60,32 +61,31 @@ export default function (actual) { const expressionBeforeSign = expression.substr(0, index) // Ignore signs that directly follow a opening bracket - if (expressionBeforeSign[expressionBeforeSign.length - 1] === "(") { return } + if (expressionBeforeSign[expressionBeforeSign.length - 1] === "(") { + return + } // Ignore signs at the beginning of the expression - if (/^\s*$/.test(expressionBeforeSign)) { return } + if (/^\s*$/.test(expressionBeforeSign)) { + return + } // Otherwise, ensure that there is a real operator preceeding them - if (/[\*/+-]\s*$/.test(expressionBeforeSign)) { return } + if (/[\*/+-]\s*$/.test(expressionBeforeSign)) { + return + } // And if not, complain complain(messages.expectedOperatorBeforeSign(symbol), decl, expressionIndex + index) return } - const beforeOk = ( - (expression[index - 1] === " " && !isWhitespace(expression[index - 2])) - || newlineBefore(expression, index - 1) - ) + const beforeOk = expression[index - 1] === " " && !isWhitespace(expression[index - 2]) || newlineBefore(expression, index - 1) if (!beforeOk) { complain(messages.expectedBefore(symbol), decl, expressionIndex + index) } - const afterOk = ( - (expression[index + 1] === " " && !isWhitespace(expression[index + 2])) - || expression[index + 1] === "\n" - || expression.substr(index + 1, 2) === "\r\n" - ) + const afterOk = expression[index + 1] === " " && !isWhitespace(expression[index + 2]) || expression[index + 1] === "\n" || expression.substr(index + 1, 2) === "\r\n" if (!afterOk) { complain(messages.expectedAfter(symbol), decl, expressionIndex + index) @@ -109,3 +109,7 @@ function newlineBefore(str, startIndex) { } return false } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-comma-newline-after/README.md b/lib/rules/function-comma-newline-after/README.md similarity index 100% rename from src/rules/function-comma-newline-after/README.md rename to lib/rules/function-comma-newline-after/README.md diff --git a/src/rules/function-comma-newline-after/__tests__/index.js b/lib/rules/function-comma-newline-after/__tests__/index.js similarity index 96% rename from src/rules/function-comma-newline-after/__tests__/index.js rename to lib/rules/function-comma-newline-after/__tests__/index.js index 92e63f66ff..935210bfe0 100644 --- a/src/rules/function-comma-newline-after/__tests__/index.js +++ b/lib/rules/function-comma-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -86,8 +86,7 @@ testRule(rule, { line: 2, column: 4, }, { - code: "a {\n transform: translate(\n 1px, /* comment (with trailing space) */ \n" + - " 1px\n );\n}", + code: "a {\n transform: translate(\n 1px, /* comment (with trailing space) */ \n" + " 1px\n );\n}", description: "eol comments", } ], }) @@ -201,8 +200,7 @@ testRule(rule, { line: 2, column: 4, }, { - code: "a {\n transform: translate(\n 1px, /* comment (with trailing space) */ \n" + - " 1px\n );\n}", + code: "a {\n transform: translate(\n 1px, /* comment (with trailing space) */ \n" + " 1px\n );\n}", description: "eol comments", } ], }) diff --git a/lib/rules/function-comma-newline-after/index.js b/lib/rules/function-comma-newline-after/index.js new file mode 100644 index 0000000000..20e7b5d167 --- /dev/null +++ b/lib/rules/function-comma-newline-after/index.js @@ -0,0 +1,38 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const functionCommaSpaceChecker = require("../functionCommaSpaceChecker") + +const ruleName = "function-comma-newline-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected newline after \",\"", + expectedAfterMultiLine: () => "Expected newline after \",\" in a multi-line function", + rejectedAfterMultiLine: () => "Unexpected whitespace after \",\" in a multi-line function", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("newline", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "always-multi-line", "never-multi-line" ], + }) + if (!validOptions) { + return + } + + functionCommaSpaceChecker({ + root, + result, + locationChecker: checker.afterOneOnly, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-comma-newline-before/README.md b/lib/rules/function-comma-newline-before/README.md similarity index 100% rename from src/rules/function-comma-newline-before/README.md rename to lib/rules/function-comma-newline-before/README.md diff --git a/src/rules/function-comma-newline-before/__tests__/index.js b/lib/rules/function-comma-newline-before/__tests__/index.js similarity index 96% rename from src/rules/function-comma-newline-before/__tests__/index.js rename to lib/rules/function-comma-newline-before/__tests__/index.js index fca75c5087..335489baf9 100644 --- a/src/rules/function-comma-newline-before/__tests__/index.js +++ b/lib/rules/function-comma-newline-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/function-comma-newline-before/index.js b/lib/rules/function-comma-newline-before/index.js similarity index 50% rename from src/rules/function-comma-newline-before/index.js rename to lib/rules/function-comma-newline-before/index.js index b244faa9e1..a974205dce 100644 --- a/src/rules/function-comma-newline-before/index.js +++ b/lib/rules/function-comma-newline-before/index.js @@ -1,30 +1,28 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { functionCommaSpaceChecker } from "../function-comma-space-after" +"use strict" -export const ruleName = "function-comma-newline-before" +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const functionCommaSpaceChecker = require("../functionCommaSpaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "function-comma-newline-before" + +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected newline before \",\"", expectedBeforeMultiLine: () => "Expected newline before \",\" in a multi-line function", rejectedBeforeMultiLine: () => "Unexpected whitespace before \",\" in a multi-line function", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("newline", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } functionCommaSpaceChecker({ root, @@ -34,3 +32,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-comma-space-after/README.md b/lib/rules/function-comma-space-after/README.md similarity index 100% rename from src/rules/function-comma-space-after/README.md rename to lib/rules/function-comma-space-after/README.md diff --git a/src/rules/function-comma-space-after/__tests__/index.js b/lib/rules/function-comma-space-after/__tests__/index.js similarity index 97% rename from src/rules/function-comma-space-after/__tests__/index.js rename to lib/rules/function-comma-space-after/__tests__/index.js index 73fda0a36a..3e0d46db0a 100644 --- a/src/rules/function-comma-space-after/__tests__/index.js +++ b/lib/rules/function-comma-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/function-comma-space-after/index.js b/lib/rules/function-comma-space-after/index.js new file mode 100644 index 0000000000..01218d19ee --- /dev/null +++ b/lib/rules/function-comma-space-after/index.js @@ -0,0 +1,39 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const functionCommaSpaceChecker = require("../functionCommaSpaceChecker") + +const ruleName = "function-comma-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected single space after \",\"", + rejectedAfter: () => "Unexpected whitespace after \",\"", + expectedAfterSingleLine: () => "Expected single space after \",\" in a single-line function", + rejectedAfterSingleLine: () => "Unexpected whitespace after \",\" in a single-line function", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never", "always-single-line", "never-single-line" ], + }) + if (!validOptions) { + return + } + + functionCommaSpaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-comma-space-before/README.md b/lib/rules/function-comma-space-before/README.md similarity index 100% rename from src/rules/function-comma-space-before/README.md rename to lib/rules/function-comma-space-before/README.md diff --git a/src/rules/function-comma-space-before/__tests__/index.js b/lib/rules/function-comma-space-before/__tests__/index.js similarity index 97% rename from src/rules/function-comma-space-before/__tests__/index.js rename to lib/rules/function-comma-space-before/__tests__/index.js index 8d6b07e751..1f5c4f9efd 100644 --- a/src/rules/function-comma-space-before/__tests__/index.js +++ b/lib/rules/function-comma-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/function-comma-space-before/index.js b/lib/rules/function-comma-space-before/index.js similarity index 52% rename from src/rules/function-comma-space-before/index.js rename to lib/rules/function-comma-space-before/index.js index 08a7efc210..62ebb96c50 100644 --- a/src/rules/function-comma-space-before/index.js +++ b/lib/rules/function-comma-space-before/index.js @@ -1,32 +1,29 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { functionCommaSpaceChecker } from "../function-comma-space-after" +"use strict" -export const ruleName = "function-comma-space-before" +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const functionCommaSpaceChecker = require("../functionCommaSpaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "function-comma-space-before" + +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected single space before \",\"", rejectedBefore: () => "Unexpected whitespace before \",\"", expectedBeforeSingleLine: () => "Expected single space before \",\" in a single-line function", rejectedBeforeSingleLine: () => "Unexpected whitespace before \",\" in a single-line function", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } functionCommaSpaceChecker({ root, @@ -36,3 +33,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-linear-gradient-no-nonstandard-direction/README.md b/lib/rules/function-linear-gradient-no-nonstandard-direction/README.md similarity index 100% rename from src/rules/function-linear-gradient-no-nonstandard-direction/README.md rename to lib/rules/function-linear-gradient-no-nonstandard-direction/README.md diff --git a/src/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js b/lib/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js similarity index 94% rename from src/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js rename to lib/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js index 73725ba85b..2991c4a205 100644 --- a/src/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js +++ b/lib/rules/function-linear-gradient-no-nonstandard-direction/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/function-linear-gradient-no-nonstandard-direction/index.js b/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js similarity index 56% rename from src/rules/function-linear-gradient-no-nonstandard-direction/index.js rename to lib/rules/function-linear-gradient-no-nonstandard-direction/index.js index 17e2292c88..f7c413c45c 100644 --- a/src/rules/function-linear-gradient-no-nonstandard-direction/index.js +++ b/lib/rules/function-linear-gradient-no-nonstandard-direction/index.js @@ -1,34 +1,40 @@ -import { - functionArgumentsSearch, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { vendor } from "postcss" +"use strict" -export const ruleName = "function-linear-gradient-no-nonstandard-direction" +const functionArgumentsSearch = require("../../utils/functionArgumentsSearch") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const postcss = require("postcss") -export const messages = ruleMessages(ruleName, { +const ruleName = "function-linear-gradient-no-nonstandard-direction" + +const messages = ruleMessages(ruleName, { rejected: "Unexpected nonstandard direction", }) function isStandardDirection(source, withToPrefix) { - const regexp = withToPrefix ? - /^to (top|left|bottom|right)(?: (top|left|bottom|right))?$/ : - /^(top|left|bottom|right)(?: (top|left|bottom|right))?$/ + const regexp = withToPrefix ? /^to (top|left|bottom|right)(?: (top|left|bottom|right))?$/ : /^(top|left|bottom|right)(?: (top|left|bottom|right))?$/ const matches = source.match(regexp) - if (!matches) { return false } - if (matches.length === 2) { return true } + if (!matches) { + return false + } + if (matches.length === 2) { + return true + } // Cannot repeat side-or-corner, e.g. "to top top" - if (matches.length === 3 && matches[1] !== matches[2]) { return true } + if (matches.length === 3 && matches[1] !== matches[2]) { + return true + } return false } -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { functionArgumentsSearch(decl.toString().toLowerCase(), "linear-gradient", (expression, expressionIndex) => { @@ -36,7 +42,9 @@ export default function (actual) { // If the first character is a number, we can assume the user intends an angle if (/[\d\.]/.test(firstArg[0])) { - if (/^[\d\.]+(?:deg|grad|rad|turn)$/.test(firstArg)) { return } + if (/^[\d\.]+(?:deg|grad|rad|turn)$/.test(firstArg)) { + return + } complain() return } @@ -44,9 +52,11 @@ export default function (actual) { // The first argument may not be a direction: it may be an angle, // or a color stop (in which case user gets default direction, "to bottom") // cf. https://drafts.csswg.org/css-images-3/#linear-gradient-syntax - if (!/left|right|top|bottom/.test(firstArg)) { return } + if (!/left|right|top|bottom/.test(firstArg)) { + return + } - const withToPrefix = !vendor.prefix(decl.value) + const withToPrefix = !postcss.vendor.prefix(decl.value) if (!isStandardDirection(firstArg, withToPrefix)) { complain() return @@ -65,3 +75,7 @@ export default function (actual) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-max-empty-lines/README.md b/lib/rules/function-max-empty-lines/README.md similarity index 100% rename from src/rules/function-max-empty-lines/README.md rename to lib/rules/function-max-empty-lines/README.md diff --git a/src/rules/function-max-empty-lines/__tests__/index.js b/lib/rules/function-max-empty-lines/__tests__/index.js similarity index 96% rename from src/rules/function-max-empty-lines/__tests__/index.js rename to lib/rules/function-max-empty-lines/__tests__/index.js index 9e422447a5..9e675cb57a 100644 --- a/src/rules/function-max-empty-lines/__tests__/index.js +++ b/lib/rules/function-max-empty-lines/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/function-max-empty-lines/index.js b/lib/rules/function-max-empty-lines/index.js new file mode 100644 index 0000000000..3f92075424 --- /dev/null +++ b/lib/rules/function-max-empty-lines/index.js @@ -0,0 +1,63 @@ +"use strict" + +const _ = require("lodash") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") + +const ruleName = "function-max-empty-lines" + +const messages = ruleMessages(ruleName, { + expected: max => `Expected no more than ${max} empty line(s)`, +}) + +const rule = function (max) { + const maxAdjacentNewlines = max + 1 + + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: max, + possible: _.isNumber, + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + if (decl.value.indexOf("(") === -1) { + return + } + + const declString = decl.toString() + const repeatLFNewLines = _.repeat("\n", maxAdjacentNewlines) + const repeatCRLFNewLines = _.repeat("\r\n", maxAdjacentNewlines) + + styleSearch({ + source: declString, + target: "\n", + functionArguments: "only", + }, match => { + if (declString.substr(match.startIndex + 1, maxAdjacentNewlines) === repeatLFNewLines || declString.substr(match.startIndex + 1, maxAdjacentNewlines * 2) === repeatCRLFNewLines) { + // Put index at `\r` if it's CRLF, otherwise leave it at `\n` + let index = match.startIndex + if (declString[index - 1] === "\r") { + index -= 1 + } + + report({ + message: messages.expected(max), + node: decl, + index, + result, + ruleName, + }) + } + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-name-case/README.md b/lib/rules/function-name-case/README.md similarity index 100% rename from src/rules/function-name-case/README.md rename to lib/rules/function-name-case/README.md diff --git a/src/rules/function-name-case/__tests__/index.js b/lib/rules/function-name-case/__tests__/index.js similarity index 98% rename from src/rules/function-name-case/__tests__/index.js rename to lib/rules/function-name-case/__tests__/index.js index 8a86f733f7..35871e02b5 100644 --- a/src/rules/function-name-case/__tests__/index.js +++ b/lib/rules/function-name-case/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/function-name-case/index.js b/lib/rules/function-name-case/index.js similarity index 52% rename from src/rules/function-name-case/index.js rename to lib/rules/function-name-case/index.js index c5fd497b77..e5af4b6fc6 100644 --- a/src/rules/function-name-case/index.js +++ b/lib/rules/function-name-case/index.js @@ -1,61 +1,62 @@ -import { - declarationValueIndex, - isStandardSyntaxFunction, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { camelCaseFunctionNames } from "../../reference/keywordSets" -import { isString } from "lodash" -import valueParser from "postcss-value-parser" +"use strict" -export const ruleName = "function-name-case" +const declarationValueIndex = require("../../utils/declarationValueIndex") +const isStandardSyntaxFunction = require("../../utils/isStandardSyntaxFunction") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") +const _ = require("lodash") +const valueParser = require("postcss-value-parser") -export const messages = ruleMessages(ruleName, { +const ruleName = "function-name-case" + +const messages = ruleMessages(ruleName, { expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, }) const mapLowercaseFunctionNamesToCamelCase = new Map() -camelCaseFunctionNames.forEach(func => { +keywordSets.camelCaseFunctionNames.forEach(func => { mapLowercaseFunctionNamesToCamelCase.set(func.toLowerCase(), func) }) -export default function (expectation, options) { +const rule = function (expectation, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "lower", - "upper", - ], + possible: [ "lower", "upper" ], }, { actual: options, possible: { - ignoreFunctions: [isString], + ignoreFunctions: [_.isString], }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { - const { value } = decl + const value = decl.value valueParser(value).walk(function (node) { - if (node.type !== "function" || !isStandardSyntaxFunction(node)) { return } + if (node.type !== "function" || !isStandardSyntaxFunction(node)) { + return + } const functionName = node.value const functionNameLowerCase = functionName.toLowerCase() const ignoreFunctions = options && options.ignoreFunctions || [] - if (ignoreFunctions.length > 0 && matchesStringOrRegExp(functionName, ignoreFunctions)) { return } + if (ignoreFunctions.length > 0 && matchesStringOrRegExp(functionName, ignoreFunctions)) { + return + } let expectedFunctionName = null - if (expectation === "lower" - && mapLowercaseFunctionNamesToCamelCase.has(functionNameLowerCase) - ) { + if (expectation === "lower" && mapLowercaseFunctionNamesToCamelCase.has(functionNameLowerCase)) { expectedFunctionName = mapLowercaseFunctionNamesToCamelCase.get(functionNameLowerCase) } else if (expectation === "lower") { expectedFunctionName = functionNameLowerCase @@ -63,7 +64,9 @@ export default function (expectation, options) { expectedFunctionName = functionName.toUpperCase() } - if (functionName === expectedFunctionName) { return } + if (functionName === expectedFunctionName) { + return + } report({ message: messages.expected(functionName, expectedFunctionName), @@ -76,3 +79,7 @@ export default function (expectation, options) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-parentheses-newline-inside/README.md b/lib/rules/function-parentheses-newline-inside/README.md similarity index 100% rename from src/rules/function-parentheses-newline-inside/README.md rename to lib/rules/function-parentheses-newline-inside/README.md diff --git a/src/rules/function-parentheses-newline-inside/__tests__/index.js b/lib/rules/function-parentheses-newline-inside/__tests__/index.js similarity index 96% rename from src/rules/function-parentheses-newline-inside/__tests__/index.js rename to lib/rules/function-parentheses-newline-inside/__tests__/index.js index 5fd5e1fdb5..68e27a9148 100644 --- a/src/rules/function-parentheses-newline-inside/__tests__/index.js +++ b/lib/rules/function-parentheses-newline-inside/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/function-parentheses-newline-inside/index.js b/lib/rules/function-parentheses-newline-inside/index.js similarity index 70% rename from src/rules/function-parentheses-newline-inside/index.js rename to lib/rules/function-parentheses-newline-inside/index.js index ccbc98935c..365c72f661 100644 --- a/src/rules/function-parentheses-newline-inside/index.js +++ b/lib/rules/function-parentheses-newline-inside/index.js @@ -1,16 +1,16 @@ -import { - declarationValueIndex, - isSingleLineString, - isStandardSyntaxFunction, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import valueParser from "postcss-value-parser" - -export const ruleName = "function-parentheses-newline-inside" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const isSingleLineString = require("../../utils/isSingleLineString") +const isStandardSyntaxFunction = require("../../utils/isStandardSyntaxFunction") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const valueParser = require("postcss-value-parser") + +const ruleName = "function-parentheses-newline-inside" + +const messages = ruleMessages(ruleName, { expectedOpening: "Expected newline after \"(\"", expectedClosing: "Expected newline before \")\"", expectedOpeningMultiLine: "Expected newline after \"(\" in a multi-line function", @@ -19,25 +19,29 @@ export const messages = ruleMessages(ruleName, { rejectedClosingMultiLine: "Unexpected whitespace before \")\" in a multi-line function", }) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { - if (decl.value.indexOf("(") === -1) { return } + if (decl.value.indexOf("(") === -1) { + return + } valueParser(decl.value).walk(valueNode => { - if (valueNode.type !== "function") { return } + if (valueNode.type !== "function") { + return + } - if (!isStandardSyntaxFunction(valueNode)) { return } + if (!isStandardSyntaxFunction(valueNode)) { + return + } const functionString = valueParser.stringify(valueNode) const isMultiLine = !isSingleLineString(functionString) @@ -90,3 +94,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-parentheses-space-inside/README.md b/lib/rules/function-parentheses-space-inside/README.md similarity index 100% rename from src/rules/function-parentheses-space-inside/README.md rename to lib/rules/function-parentheses-space-inside/README.md diff --git a/src/rules/function-parentheses-space-inside/__tests__/index.js b/lib/rules/function-parentheses-space-inside/__tests__/index.js similarity index 98% rename from src/rules/function-parentheses-space-inside/__tests__/index.js rename to lib/rules/function-parentheses-space-inside/__tests__/index.js index e79ec9def8..36608a25ca 100644 --- a/src/rules/function-parentheses-space-inside/__tests__/index.js +++ b/lib/rules/function-parentheses-space-inside/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/function-parentheses-space-inside/index.js b/lib/rules/function-parentheses-space-inside/index.js similarity index 72% rename from src/rules/function-parentheses-space-inside/index.js rename to lib/rules/function-parentheses-space-inside/index.js index d81fc32205..1ef0200e42 100644 --- a/src/rules/function-parentheses-space-inside/index.js +++ b/lib/rules/function-parentheses-space-inside/index.js @@ -1,16 +1,16 @@ -import { - declarationValueIndex, - isSingleLineString, - isStandardSyntaxFunction, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import valueParser from "postcss-value-parser" - -export const ruleName = "function-parentheses-space-inside" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const isSingleLineString = require("../../utils/isSingleLineString") +const isStandardSyntaxFunction = require("../../utils/isStandardSyntaxFunction") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const valueParser = require("postcss-value-parser") + +const ruleName = "function-parentheses-space-inside" + +const messages = ruleMessages(ruleName, { expectedOpening: "Expected single space after \"(\"", rejectedOpening: "Unexpected whitespace after \"(\"", expectedClosing: "Expected single space before \")\"", @@ -21,26 +21,29 @@ export const messages = ruleMessages(ruleName, { rejectedClosingSingleLine: "Unexpected whitespace before \")\" in a single-line function", }) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { - if (decl.value.indexOf("(") === -1) { return } + if (decl.value.indexOf("(") === -1) { + return + } valueParser(decl.value).walk(valueNode => { - if (valueNode.type !== "function") { return } + if (valueNode.type !== "function") { + return + } - if (!isStandardSyntaxFunction(valueNode)) { return } + if (!isStandardSyntaxFunction(valueNode)) { + return + } const functionString = valueParser.stringify(valueNode) const isSingleLine = isSingleLineString(functionString) @@ -98,3 +101,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-url-data-uris/README.md b/lib/rules/function-url-data-uris/README.md similarity index 100% rename from src/rules/function-url-data-uris/README.md rename to lib/rules/function-url-data-uris/README.md diff --git a/src/rules/function-url-data-uris/__tests__/index.js b/lib/rules/function-url-data-uris/__tests__/index.js similarity index 98% rename from src/rules/function-url-data-uris/__tests__/index.js rename to lib/rules/function-url-data-uris/__tests__/index.js index 9f4ed2a3d0..b341411fef 100644 --- a/src/rules/function-url-data-uris/__tests__/index.js +++ b/lib/rules/function-url-data-uris/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/function-url-data-uris/index.js b/lib/rules/function-url-data-uris/index.js new file mode 100644 index 0000000000..4fefe3a128 --- /dev/null +++ b/lib/rules/function-url-data-uris/index.js @@ -0,0 +1,61 @@ +"use strict" + +const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue") +const isVariable = require("../../utils/isVariable") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const valueParser = require("postcss-value-parser") + +const ruleName = "function-url-data-uris" + +const messages = ruleMessages(ruleName, { + expected: "Expected a data URI", + rejected: "Unexpected data URI", +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + root.walkDecls(function (decl) { + valueParser(decl.value).walk(valueNode => { + if (valueNode.type !== "function" || valueNode.value.toLowerCase() !== "url" || !valueNode.nodes.length > 0) { + return + } + + const urlValueNode = valueNode.nodes[0] + + if (!urlValueNode.value || !isStandardSyntaxValue(urlValueNode.value) || isVariable(urlValueNode.value)) { + return + } + + const valueContainDataUris = urlValueNode.value.toLowerCase().indexOf("data:") === 0 + const needUrlDataUris = expectation === "always" + + if (valueContainDataUris && needUrlDataUris || !valueContainDataUris && !needUrlDataUris) { + return + } + + const message = needUrlDataUris ? messages.expected : messages.rejected + + report({ + message, + node: decl, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-url-no-scheme-relative/README.md b/lib/rules/function-url-no-scheme-relative/README.md similarity index 100% rename from src/rules/function-url-no-scheme-relative/README.md rename to lib/rules/function-url-no-scheme-relative/README.md diff --git a/src/rules/function-url-no-scheme-relative/__tests__/index.js b/lib/rules/function-url-no-scheme-relative/__tests__/index.js similarity index 92% rename from src/rules/function-url-no-scheme-relative/__tests__/index.js rename to lib/rules/function-url-no-scheme-relative/__tests__/index.js index 171375bd34..6336f5ba9f 100644 --- a/src/rules/function-url-no-scheme-relative/__tests__/index.js +++ b/lib/rules/function-url-no-scheme-relative/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/function-url-no-scheme-relative/index.js b/lib/rules/function-url-no-scheme-relative/index.js new file mode 100644 index 0000000000..96558b8ef8 --- /dev/null +++ b/lib/rules/function-url-no-scheme-relative/index.js @@ -0,0 +1,45 @@ +"use strict" + +const functionArgumentsSearch = require("../../utils/functionArgumentsSearch") +const isStandardSyntaxUrl = require("../../utils/isStandardSyntaxUrl") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") + +const ruleName = "function-url-no-scheme-relative" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected scheme-relative url", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkDecls(function (decl) { + functionArgumentsSearch(decl.toString().toLowerCase(), "url", (args, index) => { + const url = _.trim(args, " '\"") + + if (!isStandardSyntaxUrl(url) || url.indexOf("//") !== 0) { + return + } + + report({ + message: messages.rejected, + node: decl, + index, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-url-quotes/README.md b/lib/rules/function-url-quotes/README.md similarity index 100% rename from src/rules/function-url-quotes/README.md rename to lib/rules/function-url-quotes/README.md diff --git a/src/rules/function-url-quotes/__tests__/index.js b/lib/rules/function-url-quotes/__tests__/index.js similarity index 98% rename from src/rules/function-url-quotes/__tests__/index.js rename to lib/rules/function-url-quotes/__tests__/index.js index d253037022..1d8ba9f82f 100644 --- a/src/rules/function-url-quotes/__tests__/index.js +++ b/lib/rules/function-url-quotes/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/function-url-quotes/index.js b/lib/rules/function-url-quotes/index.js similarity index 70% rename from src/rules/function-url-quotes/index.js rename to lib/rules/function-url-quotes/index.js index 184bc33914..ee2e62d003 100644 --- a/src/rules/function-url-quotes/index.js +++ b/lib/rules/function-url-quotes/index.js @@ -1,29 +1,26 @@ -import { - atRuleParamIndex, - functionArgumentsSearch, - isStandardSyntaxUrl, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" +"use strict" -export const ruleName = "function-url-quotes" +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const functionArgumentsSearch = require("../../utils/functionArgumentsSearch") +const isStandardSyntaxUrl = require("../../utils/isStandardSyntaxUrl") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") -export const messages = ruleMessages(ruleName, { +const ruleName = "function-url-quotes" + +const messages = ruleMessages(ruleName, { expected: () => "Expected quotes", rejected: () => "Unexpected quotes", }) -export default function (expectation, options) { +const rule = function (expectation, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - ], + possible: [ "always", "never" ], }, { actual: options, possible: { @@ -31,7 +28,9 @@ export default function (expectation, options) { }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkAtRules(checkStatement) root.walkRules(checkStatement) @@ -65,7 +64,9 @@ export default function (expectation, options) { let shouldHasQuotes = expectation === "always" const leftTrimmedArgs = args.trimLeft() - if (!isStandardSyntaxUrl(leftTrimmedArgs)) { return } + if (!isStandardSyntaxUrl(leftTrimmedArgs)) { + return + } const complaintIndex = index + args.length - leftTrimmedArgs.length const hasQuotes = leftTrimmedArgs[0] === "'" || leftTrimmedArgs[0] === "\"" @@ -76,10 +77,14 @@ export default function (expectation, options) { } if (shouldHasQuotes) { - if (hasQuotes) { return } + if (hasQuotes) { + return + } complain(messages.expected(functionName), node, complaintIndex) } else { - if (!hasQuotes) { return } + if (!hasQuotes) { + return + } complain(messages.rejected(functionName), node, complaintIndex) } } @@ -95,3 +100,7 @@ export default function (expectation, options) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-url-scheme-whitelist/README.md b/lib/rules/function-url-scheme-whitelist/README.md similarity index 100% rename from src/rules/function-url-scheme-whitelist/README.md rename to lib/rules/function-url-scheme-whitelist/README.md diff --git a/src/rules/function-url-scheme-whitelist/__tests__/index.js b/lib/rules/function-url-scheme-whitelist/__tests__/index.js similarity index 96% rename from src/rules/function-url-scheme-whitelist/__tests__/index.js rename to lib/rules/function-url-scheme-whitelist/__tests__/index.js index 2f52c2422d..4e537b91f7 100644 --- a/src/rules/function-url-scheme-whitelist/__tests__/index.js +++ b/lib/rules/function-url-scheme-whitelist/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/function-url-scheme-whitelist/index.js b/lib/rules/function-url-scheme-whitelist/index.js new file mode 100644 index 0000000000..370c79d38b --- /dev/null +++ b/lib/rules/function-url-scheme-whitelist/index.js @@ -0,0 +1,73 @@ +"use strict" + +const containsString = require("../../utils/containsString") +const functionArgumentsSearch = require("../../utils/functionArgumentsSearch") +const isStandardSyntaxUrl = require("../../utils/isStandardSyntaxUrl") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const parse = require("url").parse + +const ruleName = "function-url-scheme-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: scheme => `Unexpected url scheme "${scheme}:"`, +}) + +const rule = function (whitelist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkDecls(function (decl) { + functionArgumentsSearch(decl.toString().toLowerCase(), "url", (args, index) => { + const unspacedUrlString = _.trim(args, " ") + if (!isStandardSyntaxUrl(unspacedUrlString)) { + return + } + const urlString = _.trim(unspacedUrlString, "'\"") + + const url = parse(urlString) + if (url.protocol === null) { + return + } + + const scheme = url.protocol.toLowerCase().slice(0, -1) // strip trailing `:` + + // The URL spec does not require a scheme to be followed by `//`, but checking + // for it allows this rule to differentiate : urls from + // : urls. `data:` scheme urls are an exception to this rule. + const slashIndex = url.protocol.length + const expectedSlashes = urlString.slice(slashIndex, slashIndex + 2) + const isSchemeLessUrl = expectedSlashes !== "//" && scheme !== "data" + if (isSchemeLessUrl) { + return + } + + const whitelistLowerCase = typeof whitelist === "string" ? whitelist.toLowerCase() : whitelist.join("|").toLowerCase().split("|") + + if (containsString(scheme, whitelistLowerCase)) { + return + } + + report({ + message: messages.rejected(scheme), + node: decl, + index, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-whitelist/README.md b/lib/rules/function-whitelist/README.md similarity index 100% rename from src/rules/function-whitelist/README.md rename to lib/rules/function-whitelist/README.md diff --git a/src/rules/function-whitelist/__tests__/index.js b/lib/rules/function-whitelist/__tests__/index.js similarity index 90% rename from src/rules/function-whitelist/__tests__/index.js rename to lib/rules/function-whitelist/__tests__/index.js index 68e8f8ab74..5aab46b272 100644 --- a/src/rules/function-whitelist/__tests__/index.js +++ b/lib/rules/function-whitelist/__tests__/index.js @@ -1,22 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "rotate", - "rgb", - "radial-gradient", - "lightness", - "color", - ]], + config: [[ "rotate", "rgb", "radial-gradient", "lightness", "color" ]], accept: [ { code: "a { color: pink; }", diff --git a/lib/rules/function-whitelist/index.js b/lib/rules/function-whitelist/index.js new file mode 100644 index 0000000000..642f4be643 --- /dev/null +++ b/lib/rules/function-whitelist/index.js @@ -0,0 +1,58 @@ +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const isStandardSyntaxFunction = require("../../utils/isStandardSyntaxFunction") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const valueParser = require("postcss-value-parser") +const postcss = require("postcss") + +const ruleName = "function-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: name => `Unexpected function "${name}"`, +}) + +const rule = function (whitelistInput) { + const whitelist = [].concat(whitelistInput) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + root.walkDecls(decl => { + const value = decl.value + + valueParser(value).walk(function (node) { + if (node.type !== "function") { + return + } + if (!isStandardSyntaxFunction(node)) { + return + } + if (matchesStringOrRegExp(postcss.vendor.unprefixed(node.value).toLowerCase(), whitelist)) { + return + } + report({ + message: messages.rejected(node.value), + node: decl, + index: declarationValueIndex(decl) + node.sourceIndex, + result, + ruleName, + }) + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/function-whitespace-after/README.md b/lib/rules/function-whitespace-after/README.md similarity index 100% rename from src/rules/function-whitespace-after/README.md rename to lib/rules/function-whitespace-after/README.md diff --git a/src/rules/function-whitespace-after/__tests__/index.js b/lib/rules/function-whitespace-after/__tests__/index.js similarity index 95% rename from src/rules/function-whitespace-after/__tests__/index.js rename to lib/rules/function-whitespace-after/__tests__/index.js index ff9f6155c9..185c037743 100644 --- a/src/rules/function-whitespace-after/__tests__/index.js +++ b/lib/rules/function-whitespace-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/function-whitespace-after/index.js b/lib/rules/function-whitespace-after/index.js similarity index 60% rename from src/rules/function-whitespace-after/index.js rename to lib/rules/function-whitespace-after/index.js index cf7b78c37b..15219f1e55 100644 --- a/src/rules/function-whitespace-after/index.js +++ b/lib/rules/function-whitespace-after/index.js @@ -1,30 +1,29 @@ -import { - isWhitespace, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" +"use strict" -export const ruleName = "function-whitespace-after" +const isWhitespace = require("../../utils/isWhitespace") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "function-whitespace-after" + +const messages = ruleMessages(ruleName, { expected: "Expected whitespace after \")\"", rejected: "Unexpected whitespace after \")\"", }) const ACCEPTABLE_AFTER_CLOSING_PAREN = new Set([ ")", ",", "}", ":", undefined ]) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - ], + possible: [ "always", "never" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { const declString = decl.toString() @@ -43,10 +42,18 @@ export default function (expectation) { if (expectation === "always") { // Allow for the next character to be a single empty space, // another closing parenthesis, a comma, or the end of the value - if (nextChar === " ") { return } - if (nextChar === "\n") { return } - if (source.substr(index + 1, 2) === "\r\n") { return } - if (ACCEPTABLE_AFTER_CLOSING_PAREN.has(nextChar)) { return } + if (nextChar === " ") { + return + } + if (nextChar === "\n") { + return + } + if (source.substr(index + 1, 2) === "\r\n") { + return + } + if (ACCEPTABLE_AFTER_CLOSING_PAREN.has(nextChar)) { + return + } report({ message: messages.expected, node, @@ -68,3 +75,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/lib/rules/functionCommaSpaceChecker.js b/lib/rules/functionCommaSpaceChecker.js new file mode 100644 index 0000000000..2caa99ee82 --- /dev/null +++ b/lib/rules/functionCommaSpaceChecker.js @@ -0,0 +1,62 @@ +"use strict" + +const declarationValueIndex = require("../utils/declarationValueIndex") +const isStandardSyntaxFunction = require("../utils/isStandardSyntaxFunction") +const report = require("../utils/report") +const _ = require("lodash") +const styleSearch = require("style-search") +const valueParser = require("postcss-value-parser") + +module.exports = function (opts) { + opts.root.walkDecls(decl => { + const declValue = _.get(decl, "raws.value.raw", decl.value) + + valueParser(declValue).walk(valueNode => { + if (valueNode.type !== "function") { + return + } + + if (!isStandardSyntaxFunction(valueNode)) { + return + } + + // Ignore `url()` arguments, which may contain data URIs or other funky stuff + if (valueNode.value.toLowerCase() === "url") { + return + } + + const functionArguments = (() => { + let result = valueParser.stringify(valueNode) + // Remove function name and opening paren + result = result.slice(valueNode.value.length + 1) + // Remove closing paren + result = result.slice(0, result.length - 1) + // 1. Remove comments including preceeding whitespace (when only succeeded by whitespace) + // 2. Remove all other comments, but leave adjacent whitespace intact + result = result.replace(/(\ *\/(\*.*\*\/(?!\S)|\/.*)|(\/(\*.*\*\/|\/.*)))/, "") + return result + })() + + styleSearch({ + source: functionArguments, + target: ",", + functionArguments: "skip", + }, match => { + opts.locationChecker({ + source: functionArguments, + index: match.startIndex, + err: message => { + const index = declarationValueIndex(decl) + valueNode.value.length + 1 + valueNode.sourceIndex + match.startIndex + report({ + index, + message, + node: decl, + result: opts.result, + ruleName: opts.checkedRuleName, + }) + }, + }) + }) + }) + }) +} diff --git a/src/rules/indentation/README.md b/lib/rules/indentation/README.md similarity index 100% rename from src/rules/indentation/README.md rename to lib/rules/indentation/README.md diff --git a/lib/rules/indentation/__tests__/at-rules.js b/lib/rules/indentation/__tests__/at-rules.js new file mode 100644 index 0000000000..0919566164 --- /dev/null +++ b/lib/rules/indentation/__tests__/at-rules.js @@ -0,0 +1,180 @@ +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") + +const rule = rules[ruleName] + +testRule(rule, { + ruleName, + config: [2], + + accept: [ { + code: "@media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + "}", + }, { + code: "@media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + "}\n" + "\n" + "@media screen {\n" + " b { color: orange; }\n" + "}", + }, { + code: "@media print {\r\n" + " a {\r\n" + " color: pink;\r\n" + " }\r\n" + "}", + }, { + code: "@media print {\r\n" + " a {\r\n" + " color: pink;\r\n" + " }\r\n" + "}\r\n" + "\r\n" + "@media screen {\r\n" + " b { color: orange; }\r\n" + "}", + } ], + + reject: [ { + code: "\n" + " @media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + "}", + + message: messages.expected("0 spaces"), + line: 2, + column: 3, + }, { + code: "@media print {\n" + "a {\n" + " color: pink;\n" + " }\n" + "}", + + message: messages.expected("2 spaces"), + line: 2, + column: 1, + }, { + code: "@media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + "}", + + message: messages.expected("4 spaces"), + line: 3, + column: 3, + }, { + code: "@media print {\n" + " a {\n" + " color: pink;\n" + "}\n" + "}", + + message: messages.expected("2 spaces"), + line: 4, + column: 1, + }, { + code: "@media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + "\t}", + + message: messages.expected("0 spaces"), + line: 5, + column: 2, + }, { + code: "\r\n" + " @media print {\r\n" + " a {\r\n" + " color: pink;\r\n" + " }\r\n" + "}", + + message: messages.expected("0 spaces"), + line: 2, + column: 3, + }, { + code: "@media print {\r\n" + "a {\r\n" + " color: pink;\r\n" + " }\r\n" + "}", + + message: messages.expected("2 spaces"), + line: 2, + column: 1, + }, { + code: "@media print {\r\n" + " a {\r\n" + " color: pink;\r\n" + " }\r\n" + "}", + + message: messages.expected("4 spaces"), + line: 3, + column: 3, + }, { + code: "@media print {\r\n" + " a {\r\n" + " color: pink;\r\n" + "}\r\n" + "}", + + message: messages.expected("2 spaces"), + line: 4, + column: 1, + }, { + code: "@media print {\r\n" + " a {\r\n" + " color: pink;\r\n" + " }\r\n" + "\t}", + + message: messages.expected("0 spaces"), + line: 5, + column: 2, + } ], +}) + +testRule(rule, { + ruleName, + config: [ "tab", { except: ["block"] } ], + + accept: [ { + code: "@media print {\n" + "\n" + "a {\n" + "\tcolor: pink;\n" + "}\n" + "\n" + "}", + }, { + code: "@media print,\n" + "\t(-webkit-min-device-pixel-ratio: 1.25),\n" + "\t(min-resolution: 120dpi) {}", + } ], + + reject: [ { + code: "@media print {\n" + "\n" + "\ta {\n" + "\tcolor: pink;\n" + "}\n" + "\n" + "}", + + message: messages.expected("0 tabs"), + line: 3, + column: 2, + }, { + code: "@media print {\n" + "\n" + "a {\n" + "color: pink;\n" + "}\n" + "\n" + "}", + + message: messages.expected("1 tab"), + line: 4, + column: 1, + }, { + code: "@media print,\n" + " (-webkit-min-device-pixel-ratio: 1.25),\n" + "\t(min-resolution: 120dpi) {}", + + description: "multi-line at-rule params", + message: messages.expected("1 tab"), + line: 2, + column: 3, + } ], +}) + +testRule(rule, { + ruleName, + config: [ 4, { except: ["param"] } ], + + accept: [{ + code: "@media print,\n" + "(-webkit-min-device-pixel-ratio: 1.25),\n" + "(min-resolution: 120dpi) {}", + }], + + reject: [{ + code: "@media print,\n" + " (-webkit-min-device-pixel-ratio: 1.25),\n" + "(min-resolution: 120dpi) {}", + + message: messages.expected("0 spaces"), + line: 2, + column: 3, + }], +}) + +testRule(rule, { + ruleName, + config: [ 2, { ignore: ["param"] } ], + + accept: [ { + code: "@media print,\n" + "(-webkit-min-device-pixel-ratio: 1.25),\n" + "(min-resolution: 120dpi) {}", + }, { + code: "@media print,\n" + " (-webkit-min-device-pixel-ratio: 1.25),\n" + "(min-resolution: 120dpi) {}", + } ], + + reject: [{ + code: "\n" + " @media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + "}", + + message: messages.expected("0 spaces"), + line: 2, + column: 3, + }], +}) + +testRule(rule, { + ruleName, + config: [ 2, { + indentClosingBrace: true, + } ], + + accept: [ { + code: "@media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + " }", + }, { + code: "@media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + " }\n" + "\n" + "@media screen {\n" + " b { color: orange; }\n" + " }", + } ], + + reject: [ { + code: "\n" + "@media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + " }", + + message: messages.expected("2 spaces"), + line: 6, + column: 2, + }, { + code: "@media print {\n" + " a {\n" + " color: pink;\n" + " }\n" + " }", + + message: messages.expected("4 spaces"), + line: 4, + column: 4, + } ], +}) diff --git a/lib/rules/indentation/__tests__/comments.js b/lib/rules/indentation/__tests__/comments.js new file mode 100644 index 0000000000..c27aae1aa2 --- /dev/null +++ b/lib/rules/indentation/__tests__/comments.js @@ -0,0 +1,63 @@ +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") + +const rule = rules[ruleName] + +testRule(rule, { + ruleName, + config: ["tab"], + + accept: [ { + code: "/* blergh */", + }, { + code: ".foo {\n" + "\t/* blergh */\n" + "\ttop: 0;\n" + "}", + }, { + code: "@media print {\n" + "\t.foo {\n" + "\t\t/* blergh */\n" + "\t\ttop: 0;\n" + "\t}\n" + "}", + }, { + code: "/* blergh */", + }, { + code: ".foo {\r\n" + "\t/* blergh */\r\n" + "\ttop: 0;\r\n" + "}", + }, { + code: "@media print {\r\n" + "\t.foo {\r\n" + "\t\t/* blergh */\r\n" + "\t\ttop: 0;\r\n" + "\t}\r\n" + "}", + } ], + + reject: [ { + code: " /* blergh */", + message: messages.expected("0 tabs"), + line: 1, + column: 2, + }, { + code: ".foo {\n" + "\t\t/* blergh */\n" + "\ttop: 0;\n" + "}", + + message: messages.expected("1 tab"), + line: 2, + column: 3, + }, { + code: "@media print {\n" + "\t.foo {\n" + "\t/* blergh */\n" + "\t\ttop: 0;\n" + "\t}\n" + "}", + + message: messages.expected("2 tabs"), + line: 3, + column: 2, + }, { + code: " /* blergh */", + message: messages.expected("0 tabs"), + line: 1, + column: 2, + }, { + code: ".foo {\r\n" + "\t\t/* blergh */\r\n" + "\ttop: 0;\r\n" + "}", + + message: messages.expected("1 tab"), + line: 2, + column: 3, + }, { + code: "@media print {\r\n" + "\t.foo {\r\n" + "\t/* blergh */\r\n" + "\t\ttop: 0;\r\n" + "\t}\r\n" + "}", + + message: messages.expected("2 tabs"), + line: 3, + column: 2, + } ], +}) diff --git a/lib/rules/indentation/__tests__/functions.js b/lib/rules/indentation/__tests__/functions.js new file mode 100644 index 0000000000..042e7d8098 --- /dev/null +++ b/lib/rules/indentation/__tests__/functions.js @@ -0,0 +1,314 @@ +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") + +const rule = rules[ruleName] + +testRule(rule, { + ruleName, + config: [2], + skipBasicChecks: true, + + accept: [ { + code: ".foo {\n" + " color: rgb(0, 0, 0);\n" + "}", + }, { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + "}", + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + ");", + description: "sass-list", + }, { + code: "$colors: (\n" + " primary: (\n" + " base: $route;\n" + " contrast: $white\n" + " )\n" + ");", + description: "nested Sass map", + }, { + code: "background:\n" + " linear-gradient(\n" + " to bottom,\n" + " transparentize($gray-dark, 1) 0%,\n" + " transparentize($gray-dark, 0.1) 100%\n" + " );", + description: "nested parenthetical inside multiline value", + }, { + code: ".foo {\r\n" + " color: rgb(0, 0, 0);\r\n" + "}", + }, { + code: ".foo {\r\n" + " color: rgb(\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );\r\n" + " top: 0;\r\n" + "}", + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + ");", + description: "sass-list", + }, { + code: "$colors: (\r\n" + " primary: (\r\n" + " base: $route;\r\n" + " contrast: $white\r\n" + " )\r\n" + ");", + description: "nested Sass map", + }, { + code: "background:\r\n" + " linear-gradient(\r\n" + " to bottom,\r\n" + " transparentize($gray-dark, 1) 0%,\r\n" + " transparentize($gray-dark, 0.1) 100%\r\n" + " );", + description: "nested parenthetical inside multiline value", + } ], + + reject: [ { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + "0,\n" + " 0\n" + " );\n" + " top: 0;\n" + "}", + message: messages.expected("4 spaces"), + line: 4, + column: 1, + }, { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + "}", + message: messages.expected("2 spaces"), + line: 6, + column: 5, + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + ");", + description: "sass-list", + message: messages.expected("2 spaces"), + line: 4, + column: 2, + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + " );", + description: "sass-list", + message: messages.expected("0 spaces"), + line: 5, + column: 3, + }, { + code: ".foo {\r\n" + " color: rgb(\r\n" + " 0,\r\n" + "0,\r\n" + " 0\r\n" + " );\r\n" + " top: 0;\r\n" + "}", + message: messages.expected("4 spaces"), + line: 4, + column: 1, + }, { + code: ".foo {\r\n" + " color: rgb(\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );\r\n" + " top: 0;\r\n" + "}", + message: messages.expected("2 spaces"), + line: 6, + column: 5, + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + ");", + description: "sass-list", + message: messages.expected("2 spaces"), + line: 4, + column: 2, + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );", + description: "sass-list", + message: messages.expected("0 spaces"), + line: 5, + column: 3, + } ], +}) + +testRule(rule, { + ruleName, + config: [ 2, { ignore: ["inside-parens"] } ], + skipBasicChecks: true, + + accept: [ { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + "}", + }, { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + "}", + }, { + code: ".foo {\n" + " color: rgb(\n" + "0,\n" + "0,\n" + "0\n" + " );\n" + "}", + }, { + code: ".foo {\n" + " color: bar(\n" + " rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " )\n" + " );\n" + "}", + }, { + code: ".foo {\n" + " color: bar(\n" + " rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " )\n" + " );\n" + "}", + }, { + code: "$tooltip-default-settings: (\n" + " tooltip-gutter: 8px 10px,\n" + " tooltip-border: 1px solid,\n" + ");", + description: "Sass maps ignored", + } ], +}) + +testRule(rule, { + ruleName, + config: [ "tab", { "indentClosingBrace": false } ], + skipBasicChecks: true, + + accept: [{ + code: "$some-list: (\n" + "\tvar: value,\n" + "\tvar: value,\n" + "\tvar: value\n" + ");", + description: "tabbed sass-list with property value pairs", + }], + + reject: [{ + code: "$some-list: (\n" + "\tvar: value,\n" + "\tvar: value,\n" + "\t\tvar: value\n" + ");", + description: "tabbed sass-list with property value pairs", + message: messages.expected("1 tab"), + line: 4, + column: 3, + }], +}) + +testRule(rule, { + ruleName, + config: [ 2, { indentInsideParens: "twice" } ], + skipBasicChecks: true, + + accept: [ { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + "}", + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + " );", + description: "sass-list", + }, { + code: ".foo {\r\n" + " color: rgb(\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );\r\n" + " top: 0;\r\n" + "}", + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );", + description: "sass-list", + } ], + + reject: [ { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + "}", + message: messages.expected("6 spaces"), + line: 4, + column: 5, + }, { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + "}", + message: messages.expected("4 spaces"), + line: 6, + column: 6, + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + " );", + description: "sass-list", + message: messages.expected("4 spaces"), + line: 4, + column: 4, + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + " );", + description: "sass-list", + message: messages.expected("2 spaces"), + line: 5, + column: 2, + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );", + description: "sass-list", + message: messages.expected("4 spaces"), + line: 4, + column: 4, + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );", + description: "sass-list", + message: messages.expected("2 spaces"), + line: 5, + column: 2, + } ], +}) + +testRule(rule, { + ruleName, + config: [ 2, { indentInsideParens: "once-at-root-twice-in-block" } ], + skipBasicChecks: true, + + accept: [ { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + "}", + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + ");", + description: "sass-list", + }, { + code: ".foo {\r\n" + " color: rgb(\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );\r\n" + " top: 0;\r\n" + "}", + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + ");", + description: "sass-list", + } ], + + reject: [ { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + "}", + message: messages.expected("6 spaces"), + line: 4, + column: 5, + }, { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + "}", + message: messages.expected("4 spaces"), + line: 6, + column: 6, + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + " );", + description: "sass-list", + message: messages.expected("0 spaces"), + line: 5, + column: 3, + }, { + code: ".foo {\r\n" + " color: rgb(\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );\r\n" + " top: 0;\r\n" + "}", + message: messages.expected("6 spaces"), + line: 4, + column: 5, + }, { + code: ".foo {\r\n" + " color: rgb(\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );\r\n" + " top: 0;\r\n" + "}", + message: messages.expected("4 spaces"), + line: 6, + column: 6, + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );", + description: "sass-list", + message: messages.expected("0 spaces"), + line: 5, + column: 3, + } ], +}) + +testRule(rule, { + ruleName, + config: [ 2, { + indentInsideParens: "once-at-root-twice-in-block", + indentClosingBrace: true, + } ], + skipBasicChecks: true, + + accept: [ { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + " }", + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + " );", + description: "sass-list", + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0\r\n" + " );", + description: "sass-list", + } ], + + reject: [ { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + " }", + message: messages.expected("6 spaces"), + line: 4, + column: 5, + }, { + code: ".foo {\n" + " color: rgb(\n" + " 0,\n" + " 0,\n" + " 0\n" + " );\n" + " top: 0;\n" + " }", + message: messages.expected("6 spaces"), + line: 6, + column: 6, + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + " );", + description: "sass-list", + message: messages.expected("2 spaces"), + line: 4, + column: 2, + }, { + code: "$some-list: (\n" + " 0,\n" + " 0,\n" + " 0\n" + ");", + description: "sass-list", + message: messages.expected("2 spaces"), + line: 5, + column: 1, + }, { + code: ".foo {\r\n" + " color: rgb(\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );\r\n" + " top: 0;\r\n" + " }", + message: messages.expected("6 spaces"), + line: 4, + column: 5, + }, { + code: ".foo {\r\n" + " color: rgb(\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );\r\n" + " top: 0;\r\n" + " }", + message: messages.expected("6 spaces"), + line: 6, + column: 6, + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + " );", + description: "sass-list", + message: messages.expected("2 spaces"), + line: 4, + column: 2, + }, { + code: "$some-list: (\r\n" + " 0,\r\n" + " 0,\r\n" + " 0\r\n" + ");", + description: "sass-list", + message: messages.expected("2 spaces"), + line: 5, + column: 1, + } ], +}) + +testRule(rule, { + ruleName, + config: [2], + syntax: "less", + skipBasicChecks: true, + + accept: [ { + code: ".foo {\n" + " .mixin(\n" + " @foo,\n" + " @bar,\n" + " @baz\n" + " );\n" + "}", + description: "Less mixin with multi-line arguments", + }, { + code: ".foo {\r\n" + " .mixin(\r\n" + " @foo,\r\n" + " @bar,\r\n" + " @baz\r\n" + " );\r\n" + "}", + description: "Less mixin with multi-line arguments", + } ], +}) diff --git a/lib/rules/indentation/__tests__/rules.js b/lib/rules/indentation/__tests__/rules.js new file mode 100644 index 0000000000..eb9aa2fda5 --- /dev/null +++ b/lib/rules/indentation/__tests__/rules.js @@ -0,0 +1,367 @@ +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") + +const rule = rules[ruleName] + +testRule(rule, { + ruleName, + config: [2], + + accept: [ { + code: "/* anything\n" + " goes\n" + "\t\t\twithin a comment */\n" + "", + }, { + code: "a { top: 0; } b { top: 1px; }", + }, { + code: "a {\n" + " top: 0;\n" + "}\n" + "b { top: 1px; bottom: 4px; }", + }, { + code: "a {\n" + " top: 0;\n" + "} b { top: 1px; }", + }, { + code: "a {\n" + " color: pink;\n" + "}", + }, { + code: "a { color: pink;\n" + "}", + }, { + code: "a {\n" + " color: pink;\n" + "} b { top: 0; }", + }, { + code: "a { color: pink;\n" + " top: 0; background: orange;\n" + "}", + }, { + code: "a {\n" + " color: pink;\n" + "}\n" + "\n" + "\n" + "b {\n" + " color: orange\n" + "}", + }, { + code: "a {\n" + " color: pink;}", + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left;\n" + " color: pink;\n" + "}", + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left\n" + " ;\n" + "}", + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + "\n" + " bottom left\n" + " ;\n" + "}", + + description: "weird empty line", + }, { + code: "a {\n" + " *top: 1px;\n" + "}", + }, { + code: "a {\n" + " _top: 1px;\n" + "}", + }, { + code: "* { top: 0; }", + }, { + code: "@media print {\n" + " * { color: pink; }\n" + "}", + }, { + code: "/* anything\r\n" + " goes\r\n" + "\t\t\twithin a comment */\r\n" + "", + }, { + code: "a {\r\n" + " top: 0;\r\n" + "}\r\n" + "b { top: 1px; bottom: 4px; }", + }, { + code: "a {\r\n" + " top: 0;\r\n" + "} b { top: 1px; }", + }, { + code: "a {\r\n" + " color: pink;\r\n" + "}", + }, { + code: "a { color: pink;\r\n" + "}", + }, { + code: "a {\r\n" + " color: pink;\r\n" + "} b { top: 0; }", + }, { + code: "a { color: pink;\r\n" + " top: 0; background: orange;\r\n" + "}", + }, { + code: "a {\r\n" + " color: pink;\r\n" + "}\r\n" + "\r\n" + "\r\n" + "b {\r\n" + " color: orange\r\n" + "}", + }, { + code: "a {\r\n" + " color: pink;}", + }, { + code: "a {\r\n" + " background-position: top left,\r\n" + " top right,\r\n" + " bottom left;\r\n" + " color: pink;\r\n" + "}", + }, { + code: "a {\r\n" + " background-position: top left,\r\n" + " top right,\r\n" + " bottom left\r\n" + " ;\r\n" + "}", + }, { + code: "a {\r\n" + " background-position: top left,\r\n" + " top right,\r\n" + "\r\n" + " bottom left\r\n" + " ;\r\n" + "}", + + description: "weird empty line", + }, { + code: "a {\r\n" + " *top: 1px;\r\n" + "}", + }, { + code: "a {\r\n" + " _top: 1px;\r\n" + "}", + }, { + code: "* { top: 0; }", + }, { + code: "@media print {\r\n" + " * { color: pink; }\r\n" + "}", + } ], + + reject: [ { + code: "\ta {\n" + " color: pink;\n" + "}", + + message: messages.expected("0 spaces"), + line: 1, + column: 2, + }, { + code: "a {\n" + " color: pink;\n" + " }", + + message: messages.expected("0 spaces"), + line: 3, + column: 3, + }, { + code: "a,\n" + "b {\n" + " color: pink;\n" + " }", + + message: messages.expected("0 spaces"), + line: 4, + column: 3, + }, { + code: "a { color: pink;\n" + " }", + + message: messages.expected("0 spaces"), + line: 2, + column: 3, + }, { + code: "a {\n" + " color: pink\n" + "}\n" + " b {\n" + " color: orange\n" + "}", + + message: messages.expected("0 spaces"), + line: 4, + column: 2, + }, { + code: "a {\n" + " color: pink\n" + "}\n" + "b {\n" + " color: orange\n" + " }", + + message: messages.expected("0 spaces"), + line: 6, + column: 2, + }, { + code: "a {\n" + "color: pink;\n" + "}", + + message: messages.expected("2 spaces"), + line: 2, + column: 1, + }, { + code: "a {\n" + "\tcolor: pink;\n" + "}", + + message: messages.expected("2 spaces"), + line: 2, + column: 2, + }, { + code: "a {\n" + " color: pink;\n" + " background: orange;\n" + "}", + + message: messages.expected("2 spaces"), + line: 3, + column: 2, + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left;\n" + " color: pink;\n" + "}", + + message: messages.expected("4 spaces"), + line: 3, + column: 3, + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left;\n" + " color: pink;\n" + "}", + + message: messages.expected("4 spaces"), + line: 4, + column: 3, + }, { + code: "@media print {\n" + " * { color: pink; }\n" + "}", + + message: messages.expected("2 spaces"), + }, { + code: "\ta {\r\n" + " color: pink;\r\n" + "}", + + message: messages.expected("0 spaces"), + line: 1, + column: 2, + }, { + code: "a {\r\n" + " color: pink;\r\n" + " }", + + message: messages.expected("0 spaces"), + line: 3, + column: 3, + }, { + code: "a,\r\n" + "b {\r\n" + " color: pink;\r\n" + " }", + + message: messages.expected("0 spaces"), + line: 4, + column: 3, + }, { + code: "a { color: pink;\r\n" + " }", + + message: messages.expected("0 spaces"), + line: 2, + column: 3, + }, { + code: "a {\r\n" + " color: pink\r\n" + "}\r\n" + " b {\r\n" + " color: orange\r\n" + "}", + + message: messages.expected("0 spaces"), + line: 4, + column: 2, + }, { + code: "a {\r\n" + " color: pink\r\n" + "}\r\n" + "b {\r\n" + " color: orange\r\n" + " }", + + message: messages.expected("0 spaces"), + line: 6, + column: 2, + }, { + code: "a {\r\n" + "color: pink;\r\n" + "}", + + message: messages.expected("2 spaces"), + line: 2, + column: 1, + }, { + code: "a {\r\n" + "\tcolor: pink;\r\n" + "}", + + message: messages.expected("2 spaces"), + line: 2, + column: 2, + }, { + code: "a {\r\n" + " color: pink;\r\n" + " background: orange;\r\n" + "}", + + message: messages.expected("2 spaces"), + line: 3, + column: 2, + }, { + code: "a {\r\n" + " background-position: top left,\r\n" + " top right,\r\n" + " bottom left;\r\n" + " color: pink;\r\n" + "}", + + message: messages.expected("4 spaces"), + line: 3, + column: 3, + }, { + code: "a {\r\n" + " background-position: top left,\r\n" + " top right,\r\n" + " bottom left;\r\n" + " color: pink;\r\n" + "}", + + message: messages.expected("4 spaces"), + line: 4, + column: 3, + }, { + code: "@media print {\r\n" + " * { color: pink; }\r\n" + "}", + + message: messages.expected("2 spaces"), + } ], +}) + +testRule(rule, { + ruleName, + config: ["tab"], + + accept: [ { + code: "", + }, { + code: "a {color: pink;}", + }, { + code: "a {\n" + "\tcolor: pink;\n" + "}", + }, { + code: "a {\n" + "\tcolor: pink;\n" + "}\n" + "\n" + "b {\n" + "\tcolor: orange\n" + "}", + }, { + code: "a {\n" + "\tcolor: pink;}", + } ], + + reject: [ { + code: "\ta {\n" + "\tcolor: pink;\n" + "}", + + message: messages.expected("0 tabs"), + line: 1, + column: 2, + }, { + code: "a {\n" + "\tcolor: pink;\n" + " }", + + message: messages.expected("0 tabs"), + line: 3, + column: 3, + }, { + code: "a {\n" + "\tcolor: pink\n" + "}\n" + " b {\n" + "\tcolor: orange\n" + "}", + + message: messages.expected("0 tabs"), + line: 4, + column: 2, + }, { + code: "a {\n" + "\tcolor: pink\n" + "}\n" + "b {\n" + "\tcolor: orange\n" + " }", + + message: messages.expected("0 tabs"), + line: 6, + column: 2, + }, { + code: "a {\n" + "color: pink;\n" + "}", + + message: messages.expected("1 tab"), + line: 2, + column: 1, + }, { + code: "a {\n" + " color: pink;\n" + "}", + + message: messages.expected("1 tab"), + line: 2, + column: 3, + }, { + code: "a {\n" + "\tcolor: pink;\n" + " background: orange;\n" + "}", + + message: messages.expected("1 tab"), + line: 3, + column: 2, + }, { + code: "a { color: pink;\n" + "top: 0; background: orange;\n" + "}", + + message: messages.expected("1 tab"), + line: 2, + column: 1, + } ], +}) + +testRule(rule, { + ruleName, + config: [ 2, { except: ["value"] } ], + + accept: [ { + code: "a {\n" + " background-position: top left, top right, bottom left;\n" + " color: pink;\n" + "}", + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left;\n" + " color: pink;\n" + "}", + } ], + + reject: [ { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left;\n" + " color: pink;\n" + "}", + + message: messages.expected("2 spaces"), + line: 3, + column: 5, + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left;\n" + " color: pink;\n" + "}", + + message: messages.expected("2 spaces"), + line: 4, + column: 5, + } ], +}) + +testRule(rule, { + ruleName, + config: [ 2, { ignore: ["value"] } ], + + accept: [ { + code: "a {\n" + " background-position: top left, top right, bottom left;\n" + " color: pink;\n" + "}", + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left;\n" + " color: pink;\n" + "}", + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left;\n" + " color: pink;\n" + "}", + }, { + code: "a {\n" + " background-position: top left,\n" + " top right,\n" + " bottom left;\n" + " color: pink;\n" + "}", + } ], + + reject: [{ + code: "\ta {\n" + " color: pink;\n" + "}", + + message: messages.expected("0 spaces"), + line: 1, + column: 2, + }], +}) + +testRule(rule, { + ruleName, + config: [ 2, { + indentClosingBrace: true, + } ], + + accept: [ { + code: "a {\n" + " color: pink;\n" + " }", + }, { + code: "a {\n" + " color: pink;\n" + " & b {\n" + " top: 0;\n" + " }\n" + " }", + } ], + + reject: [ { + code: "a {\n" + " color: pink;\n" + "}", + message: messages.expected("2 spaces"), + line: 3, + column: 1, + }, { + code: "a {\n" + " color: pink;\n" + " & b {\n" + " top: 0;\n" + " }\n" + " }", + message: messages.expected("4 spaces"), + line: 5, + column: 4, + } ], +}) diff --git a/lib/rules/indentation/__tests__/selectors.js b/lib/rules/indentation/__tests__/selectors.js new file mode 100644 index 0000000000..26a6ea4771 --- /dev/null +++ b/lib/rules/indentation/__tests__/selectors.js @@ -0,0 +1,96 @@ +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") + +const rule = rules[ruleName] + +testRule(rule, { + ruleName, + config: [2], + skipBasicChecks: true, + + accept: [ { + code: "a { color: pink; }", + }, { + code: "a,\n" + "b { color: pink; }", + }, { + code: "a,\n" + "b,\n" + "c { color: pink; }", + }, { + code: "@media print {\n" + " a,\n" + " b { color: pink;}\n" + "}", + }, { + code: "a {\n" + " @nest b & ,\n" + " &.foo {\n" + " color: pink;\n" + " }\n" + "}", + }, { + code: "a,\r\n" + "b { color: pink; }", + }, { + code: "a,\r\n" + "b,\r\n" + "c { color: pink; }", + }, { + code: "@media print {\r\n" + " a,\r\n" + " b { color: pink;}\r\n" + "}", + }, { + code: "a {\r\n" + " @nest b & ,\r\n" + " &.foo {\r\n" + " color: pink;\r\n" + " }\r\n" + "}", + } ], + + reject: [ { + code: "a,\n" + " b { color: pink; }", + + message: messages.expected("0 spaces"), + line: 2, + column: 3, + }, { + code: "a,\n" + "b,\n" + " c { color: pink; }", + + message: messages.expected("0 spaces"), + line: 3, + column: 2, + }, { + code: "@media print {\n" + " a,\n" + "b { color: pink;}\n" + "}", + + message: messages.expected("2 spaces"), + line: 3, + column: 1, + }, { + code: "@media print {\n" + " a,\n" + " b { color: pink;}\n" + "}", + + message: messages.expected("2 spaces"), + line: 3, + column: 4, + }, { + code: "@media print {\n" + " a,\n" + " b { color: pink;}\n" + "}", + + message: messages.expected("2 spaces"), + line: 2, + column: 4, + }, { + code: "a,\r\n" + " b { color: pink; }", + + message: messages.expected("0 spaces"), + line: 2, + column: 3, + }, { + code: "a,\r\n" + "b,\r\n" + " c { color: pink; }", + + message: messages.expected("0 spaces"), + line: 3, + column: 2, + }, { + code: "@media print {\r\n" + " a,\r\n" + "b { color: pink;}\r\n" + "}", + + message: messages.expected("2 spaces"), + line: 3, + column: 1, + }, { + code: "@media print {\r\n" + " a,\r\n" + " b { color: pink;}\r\n" + "}", + + message: messages.expected("2 spaces"), + line: 3, + column: 4, + }, { + code: "@media print {\r\n" + " a,\r\n" + " b { color: pink;}\r\n" + "}", + + message: messages.expected("2 spaces"), + line: 2, + column: 4, + } ], +}) diff --git a/src/rules/indentation/index.js b/lib/rules/indentation/index.js similarity index 68% rename from src/rules/indentation/index.js rename to lib/rules/indentation/index.js index 2242ea92cf..91e7160507 100644 --- a/src/rules/indentation/index.js +++ b/lib/rules/indentation/index.js @@ -1,20 +1,16 @@ -import { - beforeBlockString, - hasBlock, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - isBoolean, - isNumber, - repeat, -} from "lodash" -import styleSearch from "style-search" - -export const ruleName = "indentation" -export const messages = ruleMessages(ruleName, { +"use strict" + +const beforeBlockString = require("../../utils/beforeBlockString") +const hasBlock = require("../../utils/hasBlock") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const styleSearch = require("style-search") + +const ruleName = "indentation" +const messages = ruleMessages(ruleName, { expected: x => `Expected indentation of ${x}`, }) @@ -23,31 +19,35 @@ export const messages = ruleMessages(ruleName, { * keyword "tab" for single `\t` * @param {object} [options] */ -export default function (space, options = {}) { +const rule = function (space) { + const options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {} + const isTab = space === "tab" - const indentChar = (isTab) ? "\t" : repeat(" ", space) - const warningWord = (isTab) ? "tab" : "space" + const indentChar = isTab ? "\t" : _.repeat(" ", space) + const warningWord = isTab ? "tab" : "space" return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: space, - possible: [ isNumber, "tab" ], + possible: [ _.isNumber, "tab" ], }, { actual: options, possible: { except: [ "block", "value", "param" ], ignore: [ "value", "param", "inside-parens" ], indentInsideParens: [ "twice", "once-at-root-twice-in-block" ], - indentClosingBrace: [isBoolean], + indentClosingBrace: [_.isBoolean], }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Cycle through all nodes using walk. root.walk(node => { const nodeLevel = indentationLevel(node) - const expectedWhitespace = repeat(indentChar, nodeLevel) + const expectedWhitespace = _.repeat(indentChar, nodeLevel) let before = node.raws.before const after = node.raws.after @@ -57,12 +57,10 @@ export default function (space, options = {}) { // or there is a newline in the `before` string. // (If there is no newline before a node, // there is no "indentation" to check.) - const inspectBefore = (root.first === node) || before.indexOf("\n") !== -1 + const inspectBefore = root.first === node || before.indexOf("\n") !== -1 // Cut out any * hacks from `before` - before = (before[before.length - 1] === "*" || before[before.length - 1] === "_") - ? before.slice(0, before.length - 1) - : before + before = before[before.length - 1] === "*" || before[before.length - 1] === "_" ? before.slice(0, before.length - 1) : before // Inspect whitespace in the `before` string that is // *after* the *last* newline character, @@ -81,13 +79,8 @@ export default function (space, options = {}) { // Only inspect `after` strings that start with a newline; // otherwise there's no indentation involved. // And check `indentClosingBrace` to see if it should be indented an extra level. - const closingBraceLevel = (options.indentClosingBrace) ? nodeLevel + 1 : nodeLevel - if ( - hasBlock(node) - && after - && after.indexOf("\n") !== -1 - && after.slice(after.lastIndexOf("\n") + 1) !== repeat(indentChar, closingBraceLevel) - ) { + const closingBraceLevel = options.indentClosingBrace ? nodeLevel + 1 : nodeLevel + if (hasBlock(node) && after && after.indexOf("\n") !== -1 && after.slice(after.lastIndexOf("\n") + 1) !== _.repeat(indentChar, closingBraceLevel)) { report({ message: messages.expected(legibleExpectation(closingBraceLevel)), node, @@ -113,8 +106,12 @@ export default function (space, options = {}) { } }) - function indentationLevel(node, level = 0) { - if (node.parent.type === "root") { return level } + function indentationLevel(node) { + const level = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0 + + if (node.parent.type === "root") { + return level + } let calculatedLevel @@ -126,11 +123,7 @@ export default function (space, options = {}) { // If options.except includes "block", // blocks are taken down one from their calculated level // (all blocks are the same level as their parents) - if ( - optionsMatches(options, "except", "block") - && (node.type === "rule" || node.type === "atrule") - && hasBlock(node) - ) { + if (optionsMatches(options, "except", "block") && (node.type === "rule" || node.type === "atrule") && hasBlock(node)) { calculatedLevel-- } @@ -138,13 +131,15 @@ export default function (space, options = {}) { } function checkValue(decl, declLevel) { - if (decl.value.indexOf("\n") === -1) { return } - if (optionsMatches(options, "ignore", "value")) { return } + if (decl.value.indexOf("\n") === -1) { + return + } + if (optionsMatches(options, "ignore", "value")) { + return + } const declString = decl.toString() - const valueLevel = (optionsMatches(options, "except", "value")) - ? declLevel - : declLevel + 1 + const valueLevel = optionsMatches(options, "except", "value") ? declLevel : declLevel + 1 checkMultilineBit(declString, valueLevel, decl) } @@ -161,18 +156,20 @@ export default function (space, options = {}) { } function checkAtRuleParams(atRule, ruleLevel) { - if (optionsMatches(options, "ignore", "param")) { return } + if (optionsMatches(options, "ignore", "param")) { + return + } // @nest rules should be treated like regular rules, not expected // to have their params (selectors) indented - const paramLevel = (optionsMatches(options, "except", "param") || atRule.name === "nest") - ? ruleLevel - : ruleLevel + 1 + const paramLevel = optionsMatches(options, "except", "param") || atRule.name === "nest" ? ruleLevel : ruleLevel + 1 checkMultilineBit(beforeBlockString(atRule).trim(), paramLevel, atRule) } function checkMultilineBit(source, newlineIndentLevel, node) { - if (source.indexOf("\n") === -1) { return } + if (source.indexOf("\n") === -1) { + return + } // `outsideParens` because function arguments and also non-standard parenthesized stuff like // Sass maps are ignored to allow for arbitrary indentation let parentheticalDepth = 0 @@ -183,10 +180,9 @@ export default function (space, options = {}) { }, (match, matchCount) => { const precedesClosingParenthesis = /^[ \t]*\)/.test(source.slice(match.startIndex + 1)) - if ( - optionsMatches(options, "ignore", "inside-parens") - && (precedesClosingParenthesis || match.insideParens) - ) { return } + if (optionsMatches(options, "ignore", "inside-parens") && (precedesClosingParenthesis || match.insideParens)) { + return + } let expectedIndentLevel = newlineIndentLevel // Modififications for parenthetical content @@ -199,9 +195,13 @@ export default function (space, options = {}) { newlineIndex-- } const followsOpeningParenthesis = /\([ \t]*$/.test(source.slice(0, newlineIndex)) - if (followsOpeningParenthesis) { parentheticalDepth += 1 } + if (followsOpeningParenthesis) { + parentheticalDepth += 1 + } expectedIndentLevel += parentheticalDepth - if (precedesClosingParenthesis) { parentheticalDepth -= 1 } + if (precedesClosingParenthesis) { + parentheticalDepth -= 1 + } switch (options.indentInsideParens) { case "twice": @@ -231,10 +231,12 @@ export default function (space, options = {}) { // check that the whitespace characters (excluding newlines) before the first // non-whitespace character equal the expected indentation const afterNewlineSpaceMatches = /^([ \t]*)\S/.exec(source.slice(match.startIndex + 1)) - if (!afterNewlineSpaceMatches) { return } + if (!afterNewlineSpaceMatches) { + return + } const afterNewlineSpace = afterNewlineSpaceMatches[1] - if (afterNewlineSpace !== repeat(indentChar, expectedIndentLevel)) { + if (afterNewlineSpace !== _.repeat(indentChar, expectedIndentLevel)) { report({ message: messages.expected(legibleExpectation(expectedIndentLevel)), node, @@ -248,10 +250,12 @@ export default function (space, options = {}) { } function legibleExpectation(level) { - const count = (isTab) ? level : level * space - const quantifiedWarningWord = (count === 1) - ? warningWord - : warningWord + "s" + const count = isTab ? level : level * space + const quantifiedWarningWord = count === 1 ? warningWord : warningWord + "s" return `${count} ${quantifiedWarningWord}` } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/lib/rules/index.js b/lib/rules/index.js new file mode 100644 index 0000000000..ef27ef6c09 --- /dev/null +++ b/lib/rules/index.js @@ -0,0 +1,349 @@ +"use strict" + +const atRuleBlacklist = require("./at-rule-blacklist") +const atRuleEmptyLineBefore = require("./at-rule-empty-line-before") +const atRuleNameCase = require("./at-rule-name-case") +const atRuleNameNewlineAfter = require("./at-rule-name-newline-after") +const atRuleNameSpaceAfter = require("./at-rule-name-space-after") +const atRuleNoUnknown = require("./at-rule-no-unknown") +const atRuleNoVendorPrefix = require("./at-rule-no-vendor-prefix") +const atRuleSemicolonNewlineAfter = require("./at-rule-semicolon-newline-after") +const atRuleWhitelist = require("./at-rule-whitelist") +const blockClosingBraceEmptyLineBefore = require("./block-closing-brace-empty-line-before") +const blockClosingBraceNewlineAfter = require("./block-closing-brace-newline-after") +const blockClosingBraceNewlineBefore = require("./block-closing-brace-newline-before") +const blockClosingBraceSpaceAfter = require("./block-closing-brace-space-after") +const blockClosingBraceSpaceBefore = require("./block-closing-brace-space-before") +const blockNoEmpty = require("./block-no-empty") +const blockNoSingleLine = require("./block-no-single-line") +const blockOpeningBraceNewlineAfter = require("./block-opening-brace-newline-after") +const blockOpeningBraceNewlineBefore = require("./block-opening-brace-newline-before") +const blockOpeningBraceSpaceAfter = require("./block-opening-brace-space-after") +const blockOpeningBraceSpaceBefore = require("./block-opening-brace-space-before") +const colorHexCase = require("./color-hex-case") +const colorHexLength = require("./color-hex-length") +const colorNamed = require("./color-named") +const colorNoHex = require("./color-no-hex") +const colorNoInvalidHex = require("./color-no-invalid-hex") +const commentEmptyLineBefore = require("./comment-empty-line-before") +const commentNoEmpty = require("./comment-no-empty") +const commentWhitespaceInside = require("./comment-whitespace-inside") +const commentWordBlacklist = require("./comment-word-blacklist") +const customMediaPattern = require("./custom-media-pattern") +const customPropertyEmptyLineBefore = require("./custom-property-empty-line-before") +const customPropertyNoOutsideRoot = require("./custom-property-no-outside-root") +const customPropertyPattern = require("./custom-property-pattern") +const declarationBangSpaceAfter = require("./declaration-bang-space-after") +const declarationBangSpaceBefore = require("./declaration-bang-space-before") +const declarationBlockNoDuplicateProperties = require("./declaration-block-no-duplicate-properties") +const declarationBlockNoIgnoredProperties = require("./declaration-block-no-ignored-properties") +const declarationBlockNoRedundantLonghandProperties = require("./declaration-block-no-redundant-longhand-properties") +const declarationBlockNoShorthandPropertyOverrides = require("./declaration-block-no-shorthand-property-overrides") +const declarationBlockPropertiesOrder = require("./declaration-block-properties-order") +const declarationBlockSemicolonNewlineAfter = require("./declaration-block-semicolon-newline-after") +const declarationBlockSemicolonNewlineBefore = require("./declaration-block-semicolon-newline-before") +const declarationBlockSemicolonSpaceAfter = require("./declaration-block-semicolon-space-after") +const declarationBlockSemicolonSpaceBefore = require("./declaration-block-semicolon-space-before") +const declarationBlockSingleLineMaxDeclarations = require("./declaration-block-single-line-max-declarations") +const declarationBlockTrailingSemicolon = require("./declaration-block-trailing-semicolon") +const declarationColonNewlineAfter = require("./declaration-colon-newline-after") +const declarationColonSpaceAfter = require("./declaration-colon-space-after") +const declarationColonSpaceBefore = require("./declaration-colon-space-before") +const declarationEmptyLineBefore = require("./declaration-empty-line-before") +const declarationNoImportant = require("./declaration-no-important") +const declarationPropertyUnitBlacklist = require("./declaration-property-unit-blacklist") +const declarationPropertyUnitWhitelist = require("./declaration-property-unit-whitelist") +const declarationPropertyValueBlacklist = require("./declaration-property-value-blacklist") +const declarationPropertyValueWhitelist = require("./declaration-property-value-whitelist") +const fontFamilyNameQuotes = require("./font-family-name-quotes") +const fontFamilyNoDuplicateNames = require("./font-family-no-duplicate-names") +const fontWeightNotation = require("./font-weight-notation") +const functionBlacklist = require("./function-blacklist") +const functionCalcNoUnspacedOperator = require("./function-calc-no-unspaced-operator") +const functionCommaNewlineAfter = require("./function-comma-newline-after") +const functionCommaNewlineBefore = require("./function-comma-newline-before") +const functionCommaSpaceAfter = require("./function-comma-space-after") +const functionCommaSpaceBefore = require("./function-comma-space-before") +const functionLinearGradientNoNonstandardDirection = require("./function-linear-gradient-no-nonstandard-direction") +const functionMaxEmptyLines = require("./function-max-empty-lines") +const functionNameCase = require("./function-name-case") +const functionParenthesesNewlineInside = require("./function-parentheses-newline-inside") +const functionParenthesesSpaceInside = require("./function-parentheses-space-inside") +const functionUrlDataUris = require("./function-url-data-uris") +const functionUrlNoSchemeRelative = require("./function-url-no-scheme-relative") +const functionUrlQuotes = require("./function-url-quotes") +const functionUrlSchemeWhitelist = require("./function-url-scheme-whitelist") +const functionWhitelist = require("./function-whitelist") +const functionWhitespaceAfter = require("./function-whitespace-after") +const indentation = require("./indentation") +const keyframeDeclarationNoImportant = require("./keyframe-declaration-no-important") +const lengthZeroNoUnit = require("./length-zero-no-unit") +const maxEmptyLines = require("./max-empty-lines") +const maxLineLength = require("./max-line-length") +const maxNestingDepth = require("./max-nesting-depth") +const mediaFeatureColonSpaceAfter = require("./media-feature-colon-space-after") +const mediaFeatureColonSpaceBefore = require("./media-feature-colon-space-before") +const mediaFeatureNameBlacklist = require("./media-feature-name-blacklist") +const mediaFeatureNameCase = require("./media-feature-name-case") +const mediaFeatureNameNoUnknown = require("./media-feature-name-no-unknown") +const mediaFeatureNameNoVendorPrefix = require("./media-feature-name-no-vendor-prefix") +const mediaFeatureNameWhitelist = require("./media-feature-name-whitelist") +const mediaFeatureNoMissingPunctuation = require("./media-feature-no-missing-punctuation") +const mediaFeatureParenthesesSpaceInside = require("./media-feature-parentheses-space-inside") +const mediaFeatureRangeOperatorSpaceAfter = require("./media-feature-range-operator-space-after") +const mediaFeatureRangeOperatorSpaceBefore = require("./media-feature-range-operator-space-before") +const mediaQueryListCommaNewlineAfter = require("./media-query-list-comma-newline-after") +const mediaQueryListCommaNewlineBefore = require("./media-query-list-comma-newline-before") +const mediaQueryListCommaSpaceAfter = require("./media-query-list-comma-space-after") +const mediaQueryListCommaSpaceBefore = require("./media-query-list-comma-space-before") +const noBrowserHacks = require("./no-browser-hacks") +const noDescendingSpecificity = require("./no-descending-specificity") +const noDuplicateSelectors = require("./no-duplicate-selectors") +const noEmptySource = require("./no-empty-source") +const noEolWhitespace = require("./no-eol-whitespace") +const noExtraSemicolons = require("./no-extra-semicolons") +const noIndistinguishableColors = require("./no-indistinguishable-colors") +const noInvalidDoubleSlashComments = require("./no-invalid-double-slash-comments") +const noMissingEndOfSourceNewline = require("./no-missing-end-of-source-newline") +const noSupportedBrowserFeatures = require("./no-unsupported-browser-features") +const noUnknownAnimations = require("./no-unknown-animations") +const numberLeadingZero = require("./number-leading-zero") +const numberMaxPrecision = require("./number-max-precision") +const numberNoTrailingZeros = require("./number-no-trailing-zeros") +const propertyBlacklist = require("./property-blacklist") +const propertyCase = require("./property-case") +const propertyNoUnknown = require("./property-no-unknown") +const propertyNoVendorPrefix = require("./property-no-vendor-prefix") +const propertyWhitelist = require("./property-whitelist") +const rootNoStandardProperties = require("./root-no-standard-properties") +const ruleNestedEmptyLineBefore = require("./rule-nested-empty-line-before") +const ruleNonNestedEmptyLineBefore = require("./rule-non-nested-empty-line-before") +const selectorAttributeBracketsSpaceInside = require("./selector-attribute-brackets-space-inside") +const selectorAttributeOperatorBlacklist = require("./selector-attribute-operator-blacklist") +const selectorAttributeOperatorSpaceAfter = require("./selector-attribute-operator-space-after") +const selectorAttributeOperatorSpaceBefore = require("./selector-attribute-operator-space-before") +const selectorAttributeOperatorWhitelist = require("./selector-attribute-operator-whitelist") +const selectorAttributeQuotes = require("./selector-attribute-quotes") +const selectorClassPattern = require("./selector-class-pattern") +const selectorCombinatorSpaceAfter = require("./selector-combinator-space-after") +const selectorCombinatorSpaceBefore = require("./selector-combinator-space-before") +const selectorDescendantCombinatorNoNonSpace = require("./selector-descendant-combinator-no-non-space") +const selectorIdPattern = require("./selector-id-pattern") +const selectorListCommaNewlineAfter = require("./selector-list-comma-newline-after") +const selectorListCommaNewlineBefore = require("./selector-list-comma-newline-before") +const selectorListCommaSpaceAfter = require("./selector-list-comma-space-after") +const selectorListCommaSpaceBefore = require("./selector-list-comma-space-before") +const selectorMaxCompoundSelectors = require("./selector-max-compound-selectors") +const selectorMaxEmptyLines = require("./selector-max-empty-lines") +const selectorMaxSpecificity = require("./selector-max-specificity") +const selectorNestedPattern = require("./selector-nested-pattern") +const selectorNoAttribute = require("./selector-no-attribute") +const selectorNoCombinator = require("./selector-no-combinator") +const selectorNoEmpty = require("./selector-no-empty") +const selectorNoId = require("./selector-no-id") +const selectorNoQualifyingType = require("./selector-no-qualifying-type") +const selectorNoType = require("./selector-no-type") +const selectorNoUniversal = require("./selector-no-universal") +const selectorNoVendorPrefix = require("./selector-no-vendor-prefix") +const selectorPseudoClassBlacklist = require("./selector-pseudo-class-blacklist") +const selectorPseudoClassCase = require("./selector-pseudo-class-case") +const selectorPseudoClassNoUnknown = require("./selector-pseudo-class-no-unknown") +const selectorPseudoClassParenthesesSpaceInside = require("./selector-pseudo-class-parentheses-space-inside") +const selectorPseudoClassWhitelist = require("./selector-pseudo-class-whitelist") +const selectorPseudoElementCase = require("./selector-pseudo-element-case") +const selectorPseudoElementColonNotation = require("./selector-pseudo-element-colon-notation") +const selectorPseudoElementNoUnknown = require("./selector-pseudo-element-no-unknown") +const selectorRootNoComposition = require("./selector-root-no-composition") +const selectorTypeCase = require("./selector-type-case") +const selectorTypeNoUnknown = require("./selector-type-no-unknown") +const shorthandPropertyNoRedundantValues = require("./shorthand-property-no-redundant-values") +const stringNoNewline = require("./string-no-newline") +const stringQuotes = require("./string-quotes") +const stylelintDisableReason = require("./stylelint-disable-reason") +const timeNoImperceptible = require("./time-no-imperceptible") +const unitBlacklist = require("./unit-blacklist") +const unitCase = require("./unit-case") +const unitNoUnknown = require("./unit-no-unknown") +const unitWhitelist = require("./unit-whitelist") +const valueKeywordCase = require("./value-keyword-case") +const valueListCommaNewlineAfter = require("./value-list-comma-newline-after") +const valueListCommaNewlineBefore = require("./value-list-comma-newline-before") +const valueListCommaSpaceAfter = require("./value-list-comma-space-after") +const valueListCommaSpaceBefore = require("./value-list-comma-space-before") +const valueListMaxEmptyLines = require("./value-list-max-empty-lines") +const valueNoVendorPrefix = require("./value-no-vendor-prefix") + +module.exports = { + "at-rule-blacklist": atRuleBlacklist, + "at-rule-empty-line-before": atRuleEmptyLineBefore, + "at-rule-name-case": atRuleNameCase, + "at-rule-name-newline-after": atRuleNameNewlineAfter, + "at-rule-name-space-after": atRuleNameSpaceAfter, + "at-rule-no-unknown": atRuleNoUnknown, + "at-rule-no-vendor-prefix": atRuleNoVendorPrefix, + "at-rule-semicolon-newline-after": atRuleSemicolonNewlineAfter, + "at-rule-whitelist": atRuleWhitelist, + "block-closing-brace-empty-line-before": blockClosingBraceEmptyLineBefore, + "block-closing-brace-newline-after": blockClosingBraceNewlineAfter, + "block-closing-brace-newline-before": blockClosingBraceNewlineBefore, + "block-closing-brace-space-after": blockClosingBraceSpaceAfter, + "block-closing-brace-space-before": blockClosingBraceSpaceBefore, + "block-no-empty": blockNoEmpty, + "block-no-single-line": blockNoSingleLine, + "block-opening-brace-newline-after": blockOpeningBraceNewlineAfter, + "block-opening-brace-newline-before": blockOpeningBraceNewlineBefore, + "block-opening-brace-space-after": blockOpeningBraceSpaceAfter, + "block-opening-brace-space-before": blockOpeningBraceSpaceBefore, + "color-hex-case": colorHexCase, + "color-hex-length": colorHexLength, + "color-named": colorNamed, + "color-no-hex": colorNoHex, + "color-no-invalid-hex": colorNoInvalidHex, + "comment-empty-line-before": commentEmptyLineBefore, + "comment-no-empty": commentNoEmpty, + "comment-whitespace-inside": commentWhitespaceInside, + "comment-word-blacklist": commentWordBlacklist, + "custom-media-pattern": customMediaPattern, + "custom-property-empty-line-before": customPropertyEmptyLineBefore, + "custom-property-no-outside-root": customPropertyNoOutsideRoot, + "custom-property-pattern": customPropertyPattern, + "declaration-bang-space-after": declarationBangSpaceAfter, + "declaration-bang-space-before": declarationBangSpaceBefore, + "declaration-block-no-duplicate-properties": declarationBlockNoDuplicateProperties, + "declaration-block-no-ignored-properties": declarationBlockNoIgnoredProperties, + "declaration-block-no-redundant-longhand-properties": declarationBlockNoRedundantLonghandProperties, + "declaration-block-no-shorthand-property-overrides": declarationBlockNoShorthandPropertyOverrides, + "declaration-block-properties-order": declarationBlockPropertiesOrder, + "declaration-block-semicolon-newline-after": declarationBlockSemicolonNewlineAfter, + "declaration-block-semicolon-newline-before": declarationBlockSemicolonNewlineBefore, + "declaration-block-semicolon-space-after": declarationBlockSemicolonSpaceAfter, + "declaration-block-semicolon-space-before": declarationBlockSemicolonSpaceBefore, + "declaration-block-single-line-max-declarations": declarationBlockSingleLineMaxDeclarations, + "declaration-block-trailing-semicolon": declarationBlockTrailingSemicolon, + "declaration-colon-newline-after": declarationColonNewlineAfter, + "declaration-colon-space-after": declarationColonSpaceAfter, + "declaration-colon-space-before": declarationColonSpaceBefore, + "declaration-empty-line-before": declarationEmptyLineBefore, + "declaration-no-important": declarationNoImportant, + "declaration-property-unit-blacklist": declarationPropertyUnitBlacklist, + "declaration-property-unit-whitelist": declarationPropertyUnitWhitelist, + "declaration-property-value-blacklist": declarationPropertyValueBlacklist, + "declaration-property-value-whitelist": declarationPropertyValueWhitelist, + "font-family-name-quotes": fontFamilyNameQuotes, + "font-family-no-duplicate-names": fontFamilyNoDuplicateNames, + "font-weight-notation": fontWeightNotation, + "function-blacklist": functionBlacklist, + "function-calc-no-unspaced-operator": functionCalcNoUnspacedOperator, + "function-comma-newline-after": functionCommaNewlineAfter, + "function-comma-newline-before": functionCommaNewlineBefore, + "function-comma-space-after": functionCommaSpaceAfter, + "function-comma-space-before": functionCommaSpaceBefore, + "function-linear-gradient-no-nonstandard-direction": functionLinearGradientNoNonstandardDirection, + "function-max-empty-lines": functionMaxEmptyLines, + "function-name-case": functionNameCase, + "function-parentheses-newline-inside": functionParenthesesNewlineInside, + "function-parentheses-space-inside": functionParenthesesSpaceInside, + "function-url-data-uris": functionUrlDataUris, + "function-url-no-scheme-relative": functionUrlNoSchemeRelative, + "function-url-quotes": functionUrlQuotes, + "function-url-scheme-whitelist": functionUrlSchemeWhitelist, + "function-whitelist": functionWhitelist, + "function-whitespace-after": functionWhitespaceAfter, + "indentation": indentation, // eslint-disable-line object-shorthand + "keyframe-declaration-no-important": keyframeDeclarationNoImportant, + "length-zero-no-unit": lengthZeroNoUnit, + "max-empty-lines": maxEmptyLines, + "max-line-length": maxLineLength, + "max-nesting-depth": maxNestingDepth, + "media-feature-colon-space-after": mediaFeatureColonSpaceAfter, + "media-feature-colon-space-before": mediaFeatureColonSpaceBefore, + "media-feature-name-blacklist": mediaFeatureNameBlacklist, + "media-feature-name-case": mediaFeatureNameCase, + "media-feature-name-no-unknown": mediaFeatureNameNoUnknown, + "media-feature-name-no-vendor-prefix": mediaFeatureNameNoVendorPrefix, + "media-feature-name-whitelist": mediaFeatureNameWhitelist, + "media-feature-no-missing-punctuation": mediaFeatureNoMissingPunctuation, + "media-feature-parentheses-space-inside": mediaFeatureParenthesesSpaceInside, + "media-feature-range-operator-space-after": mediaFeatureRangeOperatorSpaceAfter, + "media-feature-range-operator-space-before": mediaFeatureRangeOperatorSpaceBefore, + "media-query-list-comma-newline-after": mediaQueryListCommaNewlineAfter, + "media-query-list-comma-newline-before": mediaQueryListCommaNewlineBefore, + "media-query-list-comma-space-after": mediaQueryListCommaSpaceAfter, + "media-query-list-comma-space-before": mediaQueryListCommaSpaceBefore, + "no-browser-hacks": noBrowserHacks, + "no-descending-specificity": noDescendingSpecificity, + "no-duplicate-selectors": noDuplicateSelectors, + "no-empty-source": noEmptySource, + "no-eol-whitespace": noEolWhitespace, + "no-extra-semicolons": noExtraSemicolons, + "no-indistinguishable-colors": noIndistinguishableColors, + "no-invalid-double-slash-comments": noInvalidDoubleSlashComments, + "no-missing-end-of-source-newline": noMissingEndOfSourceNewline, + "no-unknown-animations": noUnknownAnimations, + "no-unsupported-browser-features": noSupportedBrowserFeatures, + "number-leading-zero": numberLeadingZero, + "number-max-precision": numberMaxPrecision, + "number-no-trailing-zeros": numberNoTrailingZeros, + "property-blacklist": propertyBlacklist, + "property-case": propertyCase, + "property-no-unknown": propertyNoUnknown, + "property-no-vendor-prefix": propertyNoVendorPrefix, + "property-whitelist": propertyWhitelist, + "root-no-standard-properties": rootNoStandardProperties, + "rule-nested-empty-line-before": ruleNestedEmptyLineBefore, + "rule-non-nested-empty-line-before": ruleNonNestedEmptyLineBefore, + "selector-attribute-brackets-space-inside": selectorAttributeBracketsSpaceInside, + "selector-attribute-operator-blacklist": selectorAttributeOperatorBlacklist, + "selector-attribute-operator-space-after": selectorAttributeOperatorSpaceAfter, + "selector-attribute-operator-space-before": selectorAttributeOperatorSpaceBefore, + "selector-attribute-operator-whitelist": selectorAttributeOperatorWhitelist, + "selector-attribute-quotes": selectorAttributeQuotes, + "selector-class-pattern": selectorClassPattern, + "selector-combinator-space-after": selectorCombinatorSpaceAfter, + "selector-combinator-space-before": selectorCombinatorSpaceBefore, + "selector-descendant-combinator-no-non-space": selectorDescendantCombinatorNoNonSpace, + "selector-id-pattern": selectorIdPattern, + "selector-list-comma-newline-after": selectorListCommaNewlineAfter, + "selector-list-comma-newline-before": selectorListCommaNewlineBefore, + "selector-list-comma-space-after": selectorListCommaSpaceAfter, + "selector-list-comma-space-before": selectorListCommaSpaceBefore, + "selector-max-compound-selectors": selectorMaxCompoundSelectors, + "selector-max-empty-lines": selectorMaxEmptyLines, + "selector-max-specificity": selectorMaxSpecificity, + "selector-nested-pattern": selectorNestedPattern, + "selector-no-attribute": selectorNoAttribute, + "selector-no-empty": selectorNoEmpty, + "selector-no-combinator": selectorNoCombinator, + "selector-no-id": selectorNoId, + "selector-no-qualifying-type": selectorNoQualifyingType, + "selector-no-type": selectorNoType, + "selector-no-universal": selectorNoUniversal, + "selector-no-vendor-prefix": selectorNoVendorPrefix, + "selector-pseudo-class-blacklist": selectorPseudoClassBlacklist, + "selector-pseudo-class-case": selectorPseudoClassCase, + "selector-pseudo-class-no-unknown": selectorPseudoClassNoUnknown, + "selector-pseudo-class-parentheses-space-inside": selectorPseudoClassParenthesesSpaceInside, + "selector-pseudo-class-whitelist": selectorPseudoClassWhitelist, + "selector-pseudo-element-case": selectorPseudoElementCase, + "selector-pseudo-element-colon-notation": selectorPseudoElementColonNotation, + "selector-pseudo-element-no-unknown": selectorPseudoElementNoUnknown, + "selector-root-no-composition": selectorRootNoComposition, + "selector-type-case": selectorTypeCase, + "selector-type-no-unknown": selectorTypeNoUnknown, + "shorthand-property-no-redundant-values": shorthandPropertyNoRedundantValues, + "string-no-newline": stringNoNewline, + "string-quotes": stringQuotes, + "stylelint-disable-reason": stylelintDisableReason, + "time-no-imperceptible": timeNoImperceptible, + "unit-blacklist": unitBlacklist, + "unit-case": unitCase, + "unit-no-unknown": unitNoUnknown, + "unit-whitelist": unitWhitelist, + "value-keyword-case": valueKeywordCase, + "value-list-comma-newline-after": valueListCommaNewlineAfter, + "value-list-comma-newline-before": valueListCommaNewlineBefore, + "value-list-comma-space-after": valueListCommaSpaceAfter, + "value-list-comma-space-before": valueListCommaSpaceBefore, + "value-list-max-empty-lines": valueListMaxEmptyLines, + "value-no-vendor-prefix": valueNoVendorPrefix, +} diff --git a/src/rules/keyframe-declaration-no-important/README.md b/lib/rules/keyframe-declaration-no-important/README.md similarity index 100% rename from src/rules/keyframe-declaration-no-important/README.md rename to lib/rules/keyframe-declaration-no-important/README.md diff --git a/src/rules/keyframe-declaration-no-important/__tests__/index.js b/lib/rules/keyframe-declaration-no-important/__tests__/index.js similarity index 90% rename from src/rules/keyframe-declaration-no-important/__tests__/index.js rename to lib/rules/keyframe-declaration-no-important/__tests__/index.js index f22ee350df..a6ff03c6bf 100644 --- a/src/rules/keyframe-declaration-no-important/__tests__/index.js +++ b/lib/rules/keyframe-declaration-no-important/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/keyframe-declaration-no-important/index.js b/lib/rules/keyframe-declaration-no-important/index.js new file mode 100644 index 0000000000..2f353f85bf --- /dev/null +++ b/lib/rules/keyframe-declaration-no-important/index.js @@ -0,0 +1,39 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "keyframe-declaration-no-important" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected !important", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkAtRules(/^(-(moz|webkit)-)?keyframes$/i, atRuleKeyframes => { + atRuleKeyframes.walkDecls(decl => { + if (!decl.important) { + return + } + report({ + message: messages.rejected, + node: decl, + word: "important", + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/length-zero-no-unit/README.md b/lib/rules/length-zero-no-unit/README.md similarity index 100% rename from src/rules/length-zero-no-unit/README.md rename to lib/rules/length-zero-no-unit/README.md diff --git a/src/rules/length-zero-no-unit/__tests__/index.js b/lib/rules/length-zero-no-unit/__tests__/index.js similarity index 96% rename from src/rules/length-zero-no-unit/__tests__/index.js rename to lib/rules/length-zero-no-unit/__tests__/index.js index 0207b8ce84..1624d18278 100644 --- a/src/rules/length-zero-no-unit/__tests__/index.js +++ b/lib/rules/length-zero-no-unit/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/length-zero-no-unit/index.js b/lib/rules/length-zero-no-unit/index.js similarity index 55% rename from src/rules/length-zero-no-unit/index.js rename to lib/rules/length-zero-no-unit/index.js index f893868294..280fd9368f 100644 --- a/src/rules/length-zero-no-unit/index.js +++ b/lib/rules/length-zero-no-unit/index.js @@ -1,39 +1,35 @@ -import { - beforeBlockString, - blurComments, - hasBlock, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - findIndex, - findLastIndex, - range, -} from "lodash" -import { lengthUnits } from "../../reference/keywordSets" -import styleSearch from "style-search" -import valueParser from "postcss-value-parser" - -export const ruleName = "length-zero-no-unit" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const beforeBlockString = require("../../utils/beforeBlockString") +const blurComments = require("../../utils/blurComments") +const hasBlock = require("../../utils/hasBlock") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const keywordSets = require("../../reference/keywordSets") +const styleSearch = require("style-search") +const valueParser = require("postcss-value-parser") + +const ruleName = "length-zero-no-unit" + +const messages = ruleMessages(ruleName, { rejected: "Unexpected unit", }) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { check(blurComments(decl.toString()), decl) }) root.walkAtRules(atRule => { - const source = (hasBlock(atRule)) - ? beforeBlockString(atRule, { noRawBefore: true }) - : atRule.toString() + const source = hasBlock(atRule) ? beforeBlockString(atRule, { noRawBefore: true }) : atRule.toString() check(source, atRule) }) @@ -56,43 +52,45 @@ export default function (actual) { // // This check prevents that from happening: we build and check against a // Set containing all the indexes that are part of a value already validated. - if (ignorableIndexes.has(index)) { return } + if (ignorableIndexes.has(index)) { + return + } - const prevValueBreakIndex = findLastIndex(value.substr(0, index), char => { + const prevValueBreakIndex = _.findLastIndex(value.substr(0, index), char => { return [ " ", ",", ")", "(", "#" ].indexOf(char) !== -1 }) // Ignore hex colors - if (value[prevValueBreakIndex] === "#") { return } + if (value[prevValueBreakIndex] === "#") { + return + } // If no prev break was found, this value starts at 0 - const valueWithZeroStart = (prevValueBreakIndex === -1) - ? 0 - : prevValueBreakIndex + 1 + const valueWithZeroStart = prevValueBreakIndex === -1 ? 0 : prevValueBreakIndex + 1 - const nextValueBreakIndex = findIndex(value.substr(valueWithZeroStart), char => { + const nextValueBreakIndex = _.findIndex(value.substr(valueWithZeroStart), char => { return [ " ", ",", ")" ].indexOf(char) !== -1 }) // If no next break was found, this value ends at the end of the string - const valueWithZeroEnd = (nextValueBreakIndex === -1) - ? value.length - : nextValueBreakIndex + valueWithZeroStart + const valueWithZeroEnd = nextValueBreakIndex === -1 ? value.length : nextValueBreakIndex + valueWithZeroStart const valueWithZero = value.slice(valueWithZeroStart, valueWithZeroEnd) const parsedValue = valueParser.unit(valueWithZero) - if (!parsedValue || (parsedValue && !parsedValue.unit)) { return } + if (!parsedValue || parsedValue && !parsedValue.unit) { + return + } // Add the indexes to ignorableIndexes so the same value will not // be checked multiple times. - range(valueWithZeroStart, valueWithZeroEnd).forEach(i => ignorableIndexes.add(i)) + _.range(valueWithZeroStart, valueWithZeroEnd).forEach(i => ignorableIndexes.add(i)) // Only pay attention if the value parses to 0 // and units with lengths - if (parseFloat(valueWithZero, 10) !== 0 - || !lengthUnits.has(parsedValue.unit.toLowerCase()) - ) { return } + if (parseFloat(valueWithZero, 10) !== 0 || !keywordSets.lengthUnits.has(parsedValue.unit.toLowerCase())) { + return + } report({ message: messages.rejected, @@ -105,3 +103,7 @@ export default function (actual) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/max-empty-lines/README.md b/lib/rules/max-empty-lines/README.md similarity index 100% rename from src/rules/max-empty-lines/README.md rename to lib/rules/max-empty-lines/README.md diff --git a/src/rules/max-empty-lines/__tests__/index.js b/lib/rules/max-empty-lines/__tests__/index.js similarity index 92% rename from src/rules/max-empty-lines/__tests__/index.js rename to lib/rules/max-empty-lines/__tests__/index.js index 3e88c5670e..f9de3064e0 100644 --- a/src/rules/max-empty-lines/__tests__/index.js +++ b/lib/rules/max-empty-lines/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/max-empty-lines/index.js b/lib/rules/max-empty-lines/index.js similarity index 59% rename from src/rules/max-empty-lines/index.js rename to lib/rules/max-empty-lines/index.js index b116bec010..f5affae5c5 100644 --- a/src/rules/max-empty-lines/index.js +++ b/lib/rules/max-empty-lines/index.js @@ -1,33 +1,32 @@ -import { - isNumber, - repeat, -} from "lodash" -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" +"use strict" -export const ruleName = "max-empty-lines" +const _ = require("lodash") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "max-empty-lines" + +const messages = ruleMessages(ruleName, { expected: max => `Expected no more than ${max} empty line(s)`, }) -export default function (max) { +const rule = function (max) { const maxAdjacentNewlines = max + 1 return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: max, - possible: isNumber, + possible: _.isNumber, }) - if (!validOptions) { return } + if (!validOptions) { + return + } const rootString = root.toString() - const repeatLFNewLines = repeat("\n", maxAdjacentNewlines) - const repeatCRLFNewLines = repeat("\r\n", maxAdjacentNewlines) + const repeatLFNewLines = _.repeat("\n", maxAdjacentNewlines) + const repeatCRLFNewLines = _.repeat("\r\n", maxAdjacentNewlines) styleSearch({ source: rootString, target: "\n" }, match => { checkMatch(rootString, match.endIndex, root) @@ -44,15 +43,19 @@ export default function (max) { }) }) - function checkMatch(source, matchEndIndex, node, offset = 0) { + function checkMatch(source, matchEndIndex, node) { + const offset = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : 0 + let violationIndex = false if (source.substr(matchEndIndex, maxAdjacentNewlines) === repeatLFNewLines) { violationIndex = matchEndIndex + maxAdjacentNewlines } else if (source.substr(matchEndIndex, maxAdjacentNewlines * 2) === repeatCRLFNewLines) { - violationIndex = matchEndIndex + (maxAdjacentNewlines * 2) + violationIndex = matchEndIndex + maxAdjacentNewlines * 2 } - if (!violationIndex) { return } + if (!violationIndex) { + return + } report({ message: messages.expected(max), @@ -64,3 +67,7 @@ export default function (max) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/max-line-length/README.md b/lib/rules/max-line-length/README.md similarity index 100% rename from src/rules/max-line-length/README.md rename to lib/rules/max-line-length/README.md diff --git a/src/rules/max-line-length/__tests__/index.js b/lib/rules/max-line-length/__tests__/index.js similarity index 95% rename from src/rules/max-line-length/__tests__/index.js rename to lib/rules/max-line-length/__tests__/index.js index cec5587d02..061581b894 100644 --- a/src/rules/max-line-length/__tests__/index.js +++ b/lib/rules/max-line-length/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/max-line-length/index.js b/lib/rules/max-line-length/index.js similarity index 73% rename from src/rules/max-line-length/index.js rename to lib/rules/max-line-length/index.js index e457136c1b..f620678323 100644 --- a/src/rules/max-line-length/index.js +++ b/lib/rules/max-line-length/index.js @@ -1,23 +1,23 @@ -import { - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isNumber } from "lodash" -import styleSearch from "style-search" - -export const ruleName = "max-line-length" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const styleSearch = require("style-search") + +const ruleName = "max-line-length" + +const messages = ruleMessages(ruleName, { expected: l => `Expected line length to be no more than ${l} characters`, }) -export default function (maxLength, options) { +const rule = function (maxLength, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: maxLength, - possible: isNumber, + possible: _.isNumber, }, { actual: options, possible: { @@ -25,7 +25,9 @@ export default function (maxLength, options) { }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Collapse all urls into something nice and short, // so they do not throw the game @@ -63,18 +65,24 @@ export default function (maxLength, options) { // If the line's length is less than or equal to the specified // max, ignore it ... So anything below is liable to be complained about - if (nextNewlineIndex - match.endIndex <= maxLength) { return } + if (nextNewlineIndex - match.endIndex <= maxLength) { + return + } const complaintIndex = nextNewlineIndex - 1 if (ignoreComments) { - if (match.insideComment) { return } + if (match.insideComment) { + return + } // This trimming business is to notice when the line starts a // comment but that comment is indented, e.g. // /* something here */ const nextTwoChars = rootString.slice(match.endIndex).trim().slice(0, 2) - if (nextTwoChars === "/*" || nextTwoChars === "//") { return } + if (nextTwoChars === "/*" || nextTwoChars === "//") { + return + } } if (ignoreNonComments) { @@ -86,7 +94,9 @@ export default function (maxLength, options) { // comment but that comment is indented, e.g. // /* something here */ const nextTwoChars = rootString.slice(match.endIndex).trim().slice(0, 2) - if (nextTwoChars !== "/*" && nextTwoChars !== "//") { return } + if (nextTwoChars !== "/*" && nextTwoChars !== "//") { + return + } return complain(complaintIndex) } @@ -100,3 +110,7 @@ export default function (maxLength, options) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/max-nesting-depth/README.md b/lib/rules/max-nesting-depth/README.md similarity index 100% rename from src/rules/max-nesting-depth/README.md rename to lib/rules/max-nesting-depth/README.md diff --git a/src/rules/max-nesting-depth/__tests__/index.js b/lib/rules/max-nesting-depth/__tests__/index.js similarity index 91% rename from src/rules/max-nesting-depth/__tests__/index.js rename to lib/rules/max-nesting-depth/__tests__/index.js index 5f412578dd..8938af56bb 100644 --- a/src/rules/max-nesting-depth/__tests__/index.js +++ b/lib/rules/max-nesting-depth/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -108,7 +108,7 @@ testRule(rule, { code: "a { @my_at_rule print { b { top: 0; }}}", message: messages.expected(1), }, { - code:"a { b { c { top: 0; }}}", + code: "a { b { c { top: 0; }}}", message: messages.expected(1), } ], }) diff --git a/src/rules/max-nesting-depth/index.js b/lib/rules/max-nesting-depth/index.js similarity index 57% rename from src/rules/max-nesting-depth/index.js rename to lib/rules/max-nesting-depth/index.js index 249f6c7d5c..6b9111d3fe 100644 --- a/src/rules/max-nesting-depth/index.js +++ b/lib/rules/max-nesting-depth/index.js @@ -1,21 +1,21 @@ -import { - hasBlock, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" +"use strict" -export const ruleName = "max-nesting-depth" +const hasBlock = require("../../utils/hasBlock") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") -export const messages = ruleMessages(ruleName, { +const ruleName = "max-nesting-depth" + +const messages = ruleMessages(ruleName, { expected: depth => `Expected nesting depth to be no more than ${depth}`, }) -export default function (max, options) { +const rule = function (max, options) { const ignoreAtRulesWithoutDeclarationBlocks = optionsMatches(options, "ignore", "at-rules-without-declaration-blocks") - const isIgnoreAtRule = (node) => (node.type === "atrule" && optionsMatches(options, "ignoreAtRules", node.name)) + const isIgnoreAtRule = node => node.type === "atrule" && optionsMatches(options, "ignoreAtRules", node.name) return (root, result) => { validateOptions(result, ruleName, { @@ -34,8 +34,12 @@ export default function (max, options) { root.walkAtRules(checkStatement) function checkStatement(statement) { - if (isIgnoreAtRule(statement)) { return } - if (!hasBlock(statement)) { return } + if (isIgnoreAtRule(statement)) { + return + } + if (!hasBlock(statement)) { + return + } const depth = nestingDepth(statement) if (depth > max) { report({ @@ -50,25 +54,21 @@ export default function (max, options) { function nestingDepth(node, level) { level = level || 0 - const { parent } = node + const parent = node.parent - if (isIgnoreAtRule(parent)) { return 0 } + if (isIgnoreAtRule(parent)) { + return 0 + } // The nesting depth level's computation has finished // when this function, recursively called, receives // a node that is not nested -- a direct child of the // root node - if ( - parent.type === "root" - || (parent.type === "atrule" && parent.parent.type === "root")) { + if (parent.type === "root" || parent.type === "atrule" && parent.parent.type === "root") { return level } - if ( - ignoreAtRulesWithoutDeclarationBlocks - && node.type === "atrule" - && node.every(child => child.type !== "decl") - ) { + if (ignoreAtRulesWithoutDeclarationBlocks && node.type === "atrule" && node.every(child => child.type !== "decl")) { return nestingDepth(parent, level) } @@ -79,3 +79,7 @@ export default function (max, options) { return nestingDepth(parent, level + 1) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-colon-space-after/README.md b/lib/rules/media-feature-colon-space-after/README.md similarity index 100% rename from src/rules/media-feature-colon-space-after/README.md rename to lib/rules/media-feature-colon-space-after/README.md diff --git a/src/rules/media-feature-colon-space-after/__tests__/index.js b/lib/rules/media-feature-colon-space-after/__tests__/index.js similarity index 94% rename from src/rules/media-feature-colon-space-after/__tests__/index.js rename to lib/rules/media-feature-colon-space-after/__tests__/index.js index 90e1d08d22..8e2fd7d368 100644 --- a/src/rules/media-feature-colon-space-after/__tests__/index.js +++ b/lib/rules/media-feature-colon-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/media-feature-colon-space-after/index.js b/lib/rules/media-feature-colon-space-after/index.js new file mode 100644 index 0000000000..65603e4989 --- /dev/null +++ b/lib/rules/media-feature-colon-space-after/index.js @@ -0,0 +1,37 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const mediaFeatureColonSpaceChecker = require("../mediaFeatureColonSpaceChecker") + +const ruleName = "media-feature-colon-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected single space after \":\"", + rejectedAfter: () => "Unexpected whitespace after \":\"", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + mediaFeatureColonSpaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-colon-space-before/README.md b/lib/rules/media-feature-colon-space-before/README.md similarity index 100% rename from src/rules/media-feature-colon-space-before/README.md rename to lib/rules/media-feature-colon-space-before/README.md diff --git a/src/rules/media-feature-colon-space-before/__tests__/index.js b/lib/rules/media-feature-colon-space-before/__tests__/index.js similarity index 94% rename from src/rules/media-feature-colon-space-before/__tests__/index.js rename to lib/rules/media-feature-colon-space-before/__tests__/index.js index cb30190ad4..338bcebc97 100644 --- a/src/rules/media-feature-colon-space-before/__tests__/index.js +++ b/lib/rules/media-feature-colon-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/media-feature-colon-space-before/index.js b/lib/rules/media-feature-colon-space-before/index.js new file mode 100644 index 0000000000..1c3c46ba64 --- /dev/null +++ b/lib/rules/media-feature-colon-space-before/index.js @@ -0,0 +1,37 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const mediaFeatureColonSpaceChecker = require("../mediaFeatureColonSpaceChecker") + +const ruleName = "media-feature-colon-space-before" + +const messages = ruleMessages(ruleName, { + expectedBefore: () => "Expected single space before \":\"", + rejectedBefore: () => "Unexpected whitespace before \":\"", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + mediaFeatureColonSpaceChecker({ + root, + result, + locationChecker: checker.before, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-name-blacklist/README.md b/lib/rules/media-feature-name-blacklist/README.md similarity index 100% rename from src/rules/media-feature-name-blacklist/README.md rename to lib/rules/media-feature-name-blacklist/README.md diff --git a/src/rules/media-feature-name-blacklist/__tests__/index.js b/lib/rules/media-feature-name-blacklist/__tests__/index.js similarity index 88% rename from src/rules/media-feature-name-blacklist/__tests__/index.js rename to lib/rules/media-feature-name-blacklist/__tests__/index.js index 0022a6fd56..24d0cffaa0 100644 --- a/src/rules/media-feature-name-blacklist/__tests__/index.js +++ b/lib/rules/media-feature-name-blacklist/__tests__/index.js @@ -1,21 +1,15 @@ -import { - messages, - ruleName, - } from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "max-width", - "--wide-viewport", - "width", - "/^my-/", - "color", - ]], + config: [[ "max-width", "--wide-viewport", "width", "/^my-/", "color" ]], accept: [ { code: "@media (min-width: 50em) { }", @@ -73,9 +67,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "feature-name", - ]], + config: [["feature-name"]], syntax: "less", accept: [ { @@ -87,10 +79,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "feature-name", - "width", - ]], + config: [[ "feature-name", "width" ]], syntax: "scss", accept: [ { diff --git a/lib/rules/media-feature-name-blacklist/index.js b/lib/rules/media-feature-name-blacklist/index.js new file mode 100644 index 0000000000..a2802fcb5f --- /dev/null +++ b/lib/rules/media-feature-name-blacklist/index.js @@ -0,0 +1,58 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const isCustomMediaQuery = require("../../utils/isCustomMediaQuery") +const isRangeContextMediaFeature = require("../../utils/isRangeContextMediaFeature") +const isStandardSyntaxMediaFeatureName = require("../../utils/isStandardSyntaxMediaFeatureName") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const mediaParser = require("postcss-media-query-parser").default + +const ruleName = "media-feature-name-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: name => `Unexpected media feature name "${name}"`, +}) + +const rule = function (blacklist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkAtRules(/^media$/i, atRule => { + mediaParser(atRule.params).walk(/^media-feature$/i, mediaFeatureNode => { + const parent = mediaFeatureNode.parent, + sourceIndex = mediaFeatureNode.sourceIndex, + value = mediaFeatureNode.value + + if (isRangeContextMediaFeature(parent.value) || !isStandardSyntaxMediaFeatureName(value) || isCustomMediaQuery(value)) { + return + } + + if (!matchesStringOrRegExp(value.toLowerCase(), blacklist)) { + return + } + + report({ + index: atRuleParamIndex(atRule) + sourceIndex, + message: messages.rejected(value), + node: atRule, + ruleName, + result, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-name-case/README.md b/lib/rules/media-feature-name-case/README.md similarity index 100% rename from src/rules/media-feature-name-case/README.md rename to lib/rules/media-feature-name-case/README.md diff --git a/src/rules/media-feature-name-case/__tests__/index.js b/lib/rules/media-feature-name-case/__tests__/index.js similarity index 97% rename from src/rules/media-feature-name-case/__tests__/index.js rename to lib/rules/media-feature-name-case/__tests__/index.js index d5f07cd6bd..a89bb88b87 100644 --- a/src/rules/media-feature-name-case/__tests__/index.js +++ b/lib/rules/media-feature-name-case/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/media-feature-name-case/index.js b/lib/rules/media-feature-name-case/index.js new file mode 100644 index 0000000000..ec78038d1f --- /dev/null +++ b/lib/rules/media-feature-name-case/index.js @@ -0,0 +1,58 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const isCustomMediaQuery = require("../../utils/isCustomMediaQuery") +const isRangeContextMediaFeature = require("../../utils/isRangeContextMediaFeature") +const isStandardSyntaxMediaFeatureName = require("../../utils/isStandardSyntaxMediaFeatureName") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const mediaParser = require("postcss-media-query-parser").default + +const ruleName = "media-feature-name-case" + +const messages = ruleMessages(ruleName, { + expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "lower", "upper" ], + }) + if (!validOptions) { + return + } + + root.walkAtRules(/^media$/i, atRule => { + mediaParser(atRule.params).walk(/^media-feature$/i, mediaFeatureNode => { + const parent = mediaFeatureNode.parent, + sourceIndex = mediaFeatureNode.sourceIndex, + value = mediaFeatureNode.value + + if (isRangeContextMediaFeature(parent.value) || !isStandardSyntaxMediaFeatureName(value) || isCustomMediaQuery(value)) { + return + } + + const expectedFeatureName = expectation === "lower" ? value.toLowerCase() : value.toUpperCase() + + if (value === expectedFeatureName) { + return + } + + report({ + index: atRuleParamIndex(atRule) + sourceIndex, + message: messages.expected(value, expectedFeatureName), + node: atRule, + ruleName, + result, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-name-no-unknown/README.md b/lib/rules/media-feature-name-no-unknown/README.md similarity index 100% rename from src/rules/media-feature-name-no-unknown/README.md rename to lib/rules/media-feature-name-no-unknown/README.md diff --git a/src/rules/media-feature-name-no-unknown/__tests__/index.js b/lib/rules/media-feature-name-no-unknown/__tests__/index.js similarity index 94% rename from src/rules/media-feature-name-no-unknown/__tests__/index.js rename to lib/rules/media-feature-name-no-unknown/__tests__/index.js index 8bda312f29..d424747796 100644 --- a/src/rules/media-feature-name-no-unknown/__tests__/index.js +++ b/lib/rules/media-feature-name-no-unknown/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/media-feature-name-no-unknown/index.js b/lib/rules/media-feature-name-no-unknown/index.js new file mode 100644 index 0000000000..dc5ee7785c --- /dev/null +++ b/lib/rules/media-feature-name-no-unknown/index.js @@ -0,0 +1,68 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const isCustomMediaQuery = require("../../utils/isCustomMediaQuery") +const isRangeContextMediaFeature = require("../../utils/isRangeContextMediaFeature") +const isStandardSyntaxMediaFeatureName = require("../../utils/isStandardSyntaxMediaFeatureName") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const keywordSets = require("../../reference/keywordSets") +const mediaParser = require("postcss-media-query-parser").default +const postcss = require("postcss") + +const ruleName = "media-feature-name-no-unknown" + +const messages = ruleMessages(ruleName, { + rejected: mediaFeatureName => `Unexpected unknown media feature name "${mediaFeatureName}"`, +}) + +const rule = function (actual, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }, { + actual: options, + possible: { + ignoreMediaFeatureNames: [_.isString], + }, + optional: true, + }) + + if (!validOptions) { + return + } + + root.walkAtRules(/^media$/i, atRule => { + mediaParser(atRule.params).walk(/^media-feature$/i, mediaFeatureNode => { + const parent = mediaFeatureNode.parent, + sourceIndex = mediaFeatureNode.sourceIndex, + value = mediaFeatureNode.value + + if (isRangeContextMediaFeature(parent.value) || !isStandardSyntaxMediaFeatureName(value) || isCustomMediaQuery(value)) { + return + } + + if (optionsMatches(options, "ignoreMediaFeatureNames", value)) { + return + } + + if (postcss.vendor.prefix(value) || keywordSets.mediaFeatureNames.has(value.toLowerCase())) { + return + } + + report({ + index: atRuleParamIndex(atRule) + sourceIndex, + message: messages.rejected(value), + node: atRule, + ruleName, + result, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-name-no-vendor-prefix/README.md b/lib/rules/media-feature-name-no-vendor-prefix/README.md similarity index 100% rename from src/rules/media-feature-name-no-vendor-prefix/README.md rename to lib/rules/media-feature-name-no-vendor-prefix/README.md diff --git a/src/rules/media-feature-name-no-vendor-prefix/__tests__/index.js b/lib/rules/media-feature-name-no-vendor-prefix/__tests__/index.js similarity index 80% rename from src/rules/media-feature-name-no-vendor-prefix/__tests__/index.js rename to lib/rules/media-feature-name-no-vendor-prefix/__tests__/index.js index 0dbdb10c78..7431889d9a 100644 --- a/src/rules/media-feature-name-no-vendor-prefix/__tests__/index.js +++ b/lib/rules/media-feature-name-no-vendor-prefix/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/media-feature-name-no-vendor-prefix/index.js b/lib/rules/media-feature-name-no-vendor-prefix/index.js new file mode 100644 index 0000000000..5b5e9b79fb --- /dev/null +++ b/lib/rules/media-feature-name-no-vendor-prefix/index.js @@ -0,0 +1,43 @@ +"use strict" + +const isAutoprefixable = require("../../utils/isAutoprefixable") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "media-feature-name-no-vendor-prefix" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected vendor-prefix", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkAtRules(/^media$/i, atRule => { + const params = atRule.params + + if (!isAutoprefixable.mediaFeatureName(params)) { + return + } + const matches = atRule.toString().match(/[a-z-]+device-pixel-ratio/ig) + matches.forEach(match => { + report({ + message: messages.rejected, + node: atRule, + word: match, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-name-whitelist/README.md b/lib/rules/media-feature-name-whitelist/README.md similarity index 100% rename from src/rules/media-feature-name-whitelist/README.md rename to lib/rules/media-feature-name-whitelist/README.md diff --git a/src/rules/media-feature-name-whitelist/__tests__/index.js b/lib/rules/media-feature-name-whitelist/__tests__/index.js similarity index 88% rename from src/rules/media-feature-name-whitelist/__tests__/index.js rename to lib/rules/media-feature-name-whitelist/__tests__/index.js index b10b49c4b4..136b3dcbae 100644 --- a/src/rules/media-feature-name-whitelist/__tests__/index.js +++ b/lib/rules/media-feature-name-whitelist/__tests__/index.js @@ -1,19 +1,15 @@ -import { - messages, - ruleName, - } from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "max-width", - "/^my-/", - "color", - ]], + config: [[ "max-width", "/^my-/", "color" ]], accept: [ { code: "@media (max-width: 50em) { }", @@ -66,10 +62,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "max-width", - "orientation", - ]], + config: [[ "max-width", "orientation" ]], syntax: "less", accept: [ { @@ -81,9 +74,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "max-width", - ]], + config: [["max-width"]], syntax: "scss", accept: [ { diff --git a/lib/rules/media-feature-name-whitelist/index.js b/lib/rules/media-feature-name-whitelist/index.js new file mode 100644 index 0000000000..4fa1139cc8 --- /dev/null +++ b/lib/rules/media-feature-name-whitelist/index.js @@ -0,0 +1,58 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const isCustomMediaQuery = require("../../utils/isCustomMediaQuery") +const isRangeContextMediaFeature = require("../../utils/isRangeContextMediaFeature") +const isStandardSyntaxMediaFeatureName = require("../../utils/isStandardSyntaxMediaFeatureName") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const mediaParser = require("postcss-media-query-parser").default + +const ruleName = "media-feature-name-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: name => `Unexpected media feature name "${name}"`, +}) + +const rule = function (whitelist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkAtRules(/^media$/i, atRule => { + mediaParser(atRule.params).walk(/^media-feature$/i, mediaFeatureNode => { + const parent = mediaFeatureNode.parent, + sourceIndex = mediaFeatureNode.sourceIndex, + value = mediaFeatureNode.value + + if (isRangeContextMediaFeature(parent.value) || !isStandardSyntaxMediaFeatureName(value) || isCustomMediaQuery(value)) { + return + } + + if (matchesStringOrRegExp(value.toLowerCase(), whitelist)) { + return + } + + report({ + index: atRuleParamIndex(atRule) + sourceIndex, + message: messages.rejected(value), + node: atRule, + ruleName, + result, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-no-missing-punctuation/README.md b/lib/rules/media-feature-no-missing-punctuation/README.md similarity index 100% rename from src/rules/media-feature-no-missing-punctuation/README.md rename to lib/rules/media-feature-no-missing-punctuation/README.md diff --git a/src/rules/media-feature-no-missing-punctuation/__tests__/index.js b/lib/rules/media-feature-no-missing-punctuation/__tests__/index.js similarity index 92% rename from src/rules/media-feature-no-missing-punctuation/__tests__/index.js rename to lib/rules/media-feature-no-missing-punctuation/__tests__/index.js index e62ada275f..f9f9b5473a 100644 --- a/src/rules/media-feature-no-missing-punctuation/__tests__/index.js +++ b/lib/rules/media-feature-no-missing-punctuation/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/media-feature-no-missing-punctuation/index.js b/lib/rules/media-feature-no-missing-punctuation/index.js similarity index 52% rename from src/rules/media-feature-no-missing-punctuation/index.js rename to lib/rules/media-feature-no-missing-punctuation/index.js index cbe0aaf061..083a2bc922 100644 --- a/src/rules/media-feature-no-missing-punctuation/index.js +++ b/lib/rules/media-feature-no-missing-punctuation/index.js @@ -1,21 +1,21 @@ -import { - atRuleParamIndex, - isStandardSyntaxMediaFeature, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import execall from "execall" -import { mediaFeaturePunctuation } from "../../reference/punctuationSets" +"use strict" -export const ruleName = "media-feature-no-missing-punctuation" +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const isStandardSyntaxMediaFeature = require("../../utils/isStandardSyntaxMediaFeature") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const execall = require("execall") +const punctuationSets = require("../../reference/punctuationSets") -export const messages = ruleMessages(ruleName, { +const ruleName = "media-feature-no-missing-punctuation" + +const messages = ruleMessages(ruleName, { rejected: "Unexpected missing punctuation", }) function isPunctuation(str) { - return mediaFeaturePunctuation.has(str) + return punctuationSets.mediaFeaturePunctuation.has(str) } function endsWithPunctuation(str) { @@ -26,17 +26,23 @@ function startsWithPunctuation(str) { return isPunctuation(str[0]) || isPunctuation(str.slice(0, 2)) } -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkAtRules(/^media$/i, atRule => { execall(/\((.*?)\)/g, atRule.params).forEach(mediaFeatureMatch => { - if (!isStandardSyntaxMediaFeature(mediaFeatureMatch.match)) { return } + if (!isStandardSyntaxMediaFeature(mediaFeatureMatch.match)) { + return + } const splitMediaFeature = mediaFeatureMatch.sub[0].trim().split(/\s+/) - if (splitMediaFeature.length === 1) { return } + if (splitMediaFeature.length === 1) { + return + } // Ignore the last one for (let i = 0, l = splitMediaFeature.length - 1; i < l; i++) { @@ -46,11 +52,19 @@ export default function (actual) { // it ends with punctuation, // the next part is punctuation, // or the next part begins with punctuation - if (isPunctuation(mediaFeaturePart)) { continue } - if (endsWithPunctuation(mediaFeaturePart)) { continue } + if (isPunctuation(mediaFeaturePart)) { + continue + } + if (endsWithPunctuation(mediaFeaturePart)) { + continue + } const nextPart = splitMediaFeature[i + 1] - if (isPunctuation(nextPart)) { continue } - if (startsWithPunctuation(nextPart)) { continue } + if (isPunctuation(nextPart)) { + continue + } + if (startsWithPunctuation(nextPart)) { + continue + } return report({ result, @@ -64,3 +78,7 @@ export default function (actual) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-parentheses-space-inside/README.md b/lib/rules/media-feature-parentheses-space-inside/README.md similarity index 100% rename from src/rules/media-feature-parentheses-space-inside/README.md rename to lib/rules/media-feature-parentheses-space-inside/README.md diff --git a/src/rules/media-feature-parentheses-space-inside/__tests__/index.js b/lib/rules/media-feature-parentheses-space-inside/__tests__/index.js similarity index 95% rename from src/rules/media-feature-parentheses-space-inside/__tests__/index.js rename to lib/rules/media-feature-parentheses-space-inside/__tests__/index.js index e4da9efcbf..31aa87143a 100644 --- a/src/rules/media-feature-parentheses-space-inside/__tests__/index.js +++ b/lib/rules/media-feature-parentheses-space-inside/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/media-feature-parentheses-space-inside/index.js b/lib/rules/media-feature-parentheses-space-inside/index.js similarity index 76% rename from src/rules/media-feature-parentheses-space-inside/index.js rename to lib/rules/media-feature-parentheses-space-inside/index.js index 4ce135d9bb..8b89f45273 100644 --- a/src/rules/media-feature-parentheses-space-inside/index.js +++ b/lib/rules/media-feature-parentheses-space-inside/index.js @@ -1,31 +1,30 @@ -import { - atRuleParamIndex, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" -import styleSearch from "style-search" +"use strict" -export const ruleName = "media-feature-parentheses-space-inside" +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "media-feature-parentheses-space-inside" + +const messages = ruleMessages(ruleName, { expectedOpening: "Expected single space after \"(\"", rejectedOpening: "Unexpected whitespace after \"(\"", expectedClosing: "Expected single space before \")\"", rejectedClosing: "Unexpected whitespace before \")\"", }) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - ], + possible: [ "always", "never" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkAtRules(/^media$/i, atRule => { // If there are comments in the params, the complete string @@ -79,3 +78,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-range-operator-space-after/README.md b/lib/rules/media-feature-range-operator-space-after/README.md similarity index 100% rename from src/rules/media-feature-range-operator-space-after/README.md rename to lib/rules/media-feature-range-operator-space-after/README.md diff --git a/src/rules/media-feature-range-operator-space-after/__tests__/index.js b/lib/rules/media-feature-range-operator-space-after/__tests__/index.js similarity index 94% rename from src/rules/media-feature-range-operator-space-after/__tests__/index.js rename to lib/rules/media-feature-range-operator-space-after/__tests__/index.js index bac4d2541d..3cdcbbb8cf 100644 --- a/src/rules/media-feature-range-operator-space-after/__tests__/index.js +++ b/lib/rules/media-feature-range-operator-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/media-feature-range-operator-space-after/index.js b/lib/rules/media-feature-range-operator-space-after/index.js similarity index 56% rename from src/rules/media-feature-range-operator-space-after/index.js rename to lib/rules/media-feature-range-operator-space-after/index.js index dc6c49a49e..bba788f86e 100644 --- a/src/rules/media-feature-range-operator-space-after/index.js +++ b/lib/rules/media-feature-range-operator-space-after/index.js @@ -1,31 +1,29 @@ -import { - atRuleParamIndex, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" +"use strict" -export const ruleName = "media-feature-range-operator-space-after" +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const findMediaOperator = require("../findMediaOperator") -export const messages = ruleMessages(ruleName, { +const ruleName = "media-feature-range-operator-space-after" + +const messages = ruleMessages(ruleName, { expectedAfter: () => "Expected single space after range operator", rejectedAfter: () => "Unexpected whitespace after range operator", }) -const rangeOperatorRegex = /[^><](>=?|<=?|=)/g - -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - ], + possible: [ "always", "never" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkAtRules(/^media$/i, atRule => { findMediaOperator(atRule, checkAfterOperator) @@ -51,12 +49,6 @@ export default function (expectation) { } } -export function findMediaOperator(atRule, cb) { - if (atRule.name.toLowerCase() !== "media") { return } - - const params = atRule.params - let match - while ((match = rangeOperatorRegex.exec(params)) !== null) { - cb(match, params, atRule) - } -} +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-feature-range-operator-space-before/README.md b/lib/rules/media-feature-range-operator-space-before/README.md similarity index 100% rename from src/rules/media-feature-range-operator-space-before/README.md rename to lib/rules/media-feature-range-operator-space-before/README.md diff --git a/src/rules/media-feature-range-operator-space-before/__tests__/index.js b/lib/rules/media-feature-range-operator-space-before/__tests__/index.js similarity index 94% rename from src/rules/media-feature-range-operator-space-before/__tests__/index.js rename to lib/rules/media-feature-range-operator-space-before/__tests__/index.js index 05a146f5ba..33e91c92ec 100644 --- a/src/rules/media-feature-range-operator-space-before/__tests__/index.js +++ b/lib/rules/media-feature-range-operator-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/media-feature-range-operator-space-before/index.js b/lib/rules/media-feature-range-operator-space-before/index.js similarity index 57% rename from src/rules/media-feature-range-operator-space-before/index.js rename to lib/rules/media-feature-range-operator-space-before/index.js index 3917c3eda6..ddc982ecbc 100644 --- a/src/rules/media-feature-range-operator-space-before/index.js +++ b/lib/rules/media-feature-range-operator-space-before/index.js @@ -1,31 +1,29 @@ -import { - atRuleParamIndex, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" +"use strict" -import { findMediaOperator } from "../media-feature-range-operator-space-after" +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const findMediaOperator = require("../findMediaOperator") -export const ruleName = "media-feature-range-operator-space-before" +const ruleName = "media-feature-range-operator-space-before" -export const messages = ruleMessages(ruleName, { +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected single space before range operator", rejectedBefore: () => "Unexpected whitespace before range operator", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - ], + possible: [ "always", "never" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkAtRules(/^media$/i, atRule => { findMediaOperator(atRule, checkBeforeOperator) @@ -50,3 +48,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-query-list-comma-newline-after/README.md b/lib/rules/media-query-list-comma-newline-after/README.md similarity index 100% rename from src/rules/media-query-list-comma-newline-after/README.md rename to lib/rules/media-query-list-comma-newline-after/README.md diff --git a/src/rules/media-query-list-comma-newline-after/__tests__/index.js b/lib/rules/media-query-list-comma-newline-after/__tests__/index.js similarity index 97% rename from src/rules/media-query-list-comma-newline-after/__tests__/index.js rename to lib/rules/media-query-list-comma-newline-after/__tests__/index.js index 1a9de4c516..987c024eed 100644 --- a/src/rules/media-query-list-comma-newline-after/__tests__/index.js +++ b/lib/rules/media-query-list-comma-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/media-query-list-comma-newline-after/index.js b/lib/rules/media-query-list-comma-newline-after/index.js similarity index 53% rename from src/rules/media-query-list-comma-newline-after/index.js rename to lib/rules/media-query-list-comma-newline-after/index.js index 832cc2ebd0..736eed4c72 100644 --- a/src/rules/media-query-list-comma-newline-after/index.js +++ b/lib/rules/media-query-list-comma-newline-after/index.js @@ -1,31 +1,29 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { mediaQueryListCommaWhitespaceChecker } from "../media-query-list-comma-space-after" +"use strict" -export const ruleName = "media-query-list-comma-newline-after" +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const mediaQueryListCommaWhitespaceChecker = require("../mediaQueryListCommaWhitespaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "media-query-list-comma-newline-after" + +const messages = ruleMessages(ruleName, { expectedAfter: () => "Expected newline after \",\"", expectedAfterMultiLine: () => "Expected newline after \",\" in a multi-line list", rejectedAfterMultiLine: () => "Unexpected whitespace after \",\" in a multi-line list", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("newline", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], + possible: [ "always", "always-multi-line", "never-multi-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Only check for the newline after the comma, while allowing // arbitrary indentation after the newline @@ -37,3 +35,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-query-list-comma-newline-before/README.md b/lib/rules/media-query-list-comma-newline-before/README.md similarity index 100% rename from src/rules/media-query-list-comma-newline-before/README.md rename to lib/rules/media-query-list-comma-newline-before/README.md diff --git a/src/rules/media-query-list-comma-newline-before/__tests__/index.js b/lib/rules/media-query-list-comma-newline-before/__tests__/index.js similarity index 97% rename from src/rules/media-query-list-comma-newline-before/__tests__/index.js rename to lib/rules/media-query-list-comma-newline-before/__tests__/index.js index c0c40e8549..2bb087670d 100644 --- a/src/rules/media-query-list-comma-newline-before/__tests__/index.js +++ b/lib/rules/media-query-list-comma-newline-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/media-query-list-comma-newline-before/index.js b/lib/rules/media-query-list-comma-newline-before/index.js new file mode 100644 index 0000000000..adfb0a2611 --- /dev/null +++ b/lib/rules/media-query-list-comma-newline-before/index.js @@ -0,0 +1,37 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const mediaQueryListCommaWhitespaceChecker = require("../mediaQueryListCommaWhitespaceChecker") + +const ruleName = "media-query-list-comma-newline-before" + +const messages = ruleMessages(ruleName, { + expectedBefore: () => "Expected newline before \",\"", + expectedBeforeMultiLine: () => "Expected newline before \",\" in a multi-line list", + rejectedBeforeMultiLine: () => "Unexpected whitespace before \",\" in a multi-line list", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("newline", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "always-multi-line", "never-multi-line" ], + }) + if (!validOptions) { + return + } + mediaQueryListCommaWhitespaceChecker({ + root, + result, + locationChecker: checker.beforeAllowingIndentation, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-query-list-comma-space-after/README.md b/lib/rules/media-query-list-comma-space-after/README.md similarity index 100% rename from src/rules/media-query-list-comma-space-after/README.md rename to lib/rules/media-query-list-comma-space-after/README.md diff --git a/src/rules/media-query-list-comma-space-after/__tests__/index.js b/lib/rules/media-query-list-comma-space-after/__tests__/index.js similarity index 97% rename from src/rules/media-query-list-comma-space-after/__tests__/index.js rename to lib/rules/media-query-list-comma-space-after/__tests__/index.js index 2b1875539c..f67f93edf2 100644 --- a/src/rules/media-query-list-comma-space-after/__tests__/index.js +++ b/lib/rules/media-query-list-comma-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/media-query-list-comma-space-after/index.js b/lib/rules/media-query-list-comma-space-after/index.js new file mode 100644 index 0000000000..e6002a581d --- /dev/null +++ b/lib/rules/media-query-list-comma-space-after/index.js @@ -0,0 +1,38 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const mediaQueryListCommaWhitespaceChecker = require("../mediaQueryListCommaWhitespaceChecker") + +const ruleName = "media-query-list-comma-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected single space after \",\"", + rejectedAfter: () => "Unexpected whitespace after \",\"", + expectedAfterSingleLine: () => "Expected single space after \",\" in a single-line list", + rejectedAfterSingleLine: () => "Unexpected whitespace after \",\" in a single-line list", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never", "always-single-line", "never-single-line" ], + }) + if (!validOptions) { + return + } + mediaQueryListCommaWhitespaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/media-query-list-comma-space-before/README.md b/lib/rules/media-query-list-comma-space-before/README.md similarity index 100% rename from src/rules/media-query-list-comma-space-before/README.md rename to lib/rules/media-query-list-comma-space-before/README.md diff --git a/src/rules/media-query-list-comma-space-before/__tests__/index.js b/lib/rules/media-query-list-comma-space-before/__tests__/index.js similarity index 97% rename from src/rules/media-query-list-comma-space-before/__tests__/index.js rename to lib/rules/media-query-list-comma-space-before/__tests__/index.js index 7094052401..ec7467f6e0 100644 --- a/src/rules/media-query-list-comma-space-before/__tests__/index.js +++ b/lib/rules/media-query-list-comma-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/media-query-list-comma-space-before/index.js b/lib/rules/media-query-list-comma-space-before/index.js similarity index 51% rename from src/rules/media-query-list-comma-space-before/index.js rename to lib/rules/media-query-list-comma-space-before/index.js index 77614acc65..951ca2ffc6 100644 --- a/src/rules/media-query-list-comma-space-before/index.js +++ b/lib/rules/media-query-list-comma-space-before/index.js @@ -1,32 +1,29 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { mediaQueryListCommaWhitespaceChecker } from "../media-query-list-comma-space-after" +"use strict" -export const ruleName = "media-query-list-comma-space-before" +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const mediaQueryListCommaWhitespaceChecker = require("../mediaQueryListCommaWhitespaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "media-query-list-comma-space-before" + +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected single space before \",\"", rejectedBefore: () => "Unexpected whitespace before \",\"", expectedBeforeSingleLine: () => "Expected single space before \",\" in a single-line list", rejectedBeforeSingleLine: () => "Unexpected whitespace before \",\" in a single-line list", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } mediaQueryListCommaWhitespaceChecker({ root, @@ -36,3 +33,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/lib/rules/mediaFeatureColonSpaceChecker.js b/lib/rules/mediaFeatureColonSpaceChecker.js new file mode 100644 index 0000000000..49a2d485ed --- /dev/null +++ b/lib/rules/mediaFeatureColonSpaceChecker.js @@ -0,0 +1,26 @@ +"use strict" + +const atRuleParamIndex = require("../utils/atRuleParamIndex") +const report = require("../utils/report") +const styleSearch = require("style-search") + +module.exports = function (opts) { + opts.root.walkAtRules(/^media$/i, atRule => { + const params = atRule.params + + styleSearch({ source: params, target: ":" }, match => { + checkColon(params, match.startIndex, atRule) + }) + }) + + function checkColon(source, index, node) { + opts.locationChecker({ source, index, err: m => report({ + message: m, + node, + index: index + atRuleParamIndex(node), + result: opts.result, + ruleName: opts.checkedRuleName, + }), + }) + } +} diff --git a/lib/rules/mediaQueryListCommaWhitespaceChecker.js b/lib/rules/mediaQueryListCommaWhitespaceChecker.js new file mode 100644 index 0000000000..5dfd987d47 --- /dev/null +++ b/lib/rules/mediaQueryListCommaWhitespaceChecker.js @@ -0,0 +1,25 @@ +"use strict" + +const atRuleParamIndex = require("../utils/atRuleParamIndex") +const report = require("../utils/report") +const styleSearch = require("style-search") + +module.exports = function (opts) { + opts.root.walkAtRules(/^media$/i, atRule => { + const params = atRule.params + styleSearch({ source: params, target: "," }, match => { + checkComma(params, match.startIndex, atRule) + }) + }) + + function checkComma(source, index, node) { + opts.locationChecker({ source, index, err: m => report({ + message: m, + node, + index: index + atRuleParamIndex(node), + result: opts.result, + ruleName: opts.checkedRuleName, + }), + }) + } +} diff --git a/src/rules/no-browser-hacks/README.md b/lib/rules/no-browser-hacks/README.md similarity index 100% rename from src/rules/no-browser-hacks/README.md rename to lib/rules/no-browser-hacks/README.md diff --git a/src/rules/no-browser-hacks/__tests__/index.js b/lib/rules/no-browser-hacks/__tests__/index.js similarity index 87% rename from src/rules/no-browser-hacks/__tests__/index.js rename to lib/rules/no-browser-hacks/__tests__/index.js index 88d1e68f66..74c219f31b 100644 --- a/src/rules/no-browser-hacks/__tests__/index.js +++ b/lib/rules/no-browser-hacks/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/no-browser-hacks/index.js b/lib/rules/no-browser-hacks/index.js similarity index 60% rename from src/rules/no-browser-hacks/index.js rename to lib/rules/no-browser-hacks/index.js index 4a9bd38a3a..195e73e68d 100644 --- a/src/rules/no-browser-hacks/index.js +++ b/lib/rules/no-browser-hacks/index.js @@ -1,28 +1,30 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import Result from "postcss/lib/result" -import { isString } from "lodash" -import stylehacks from "stylehacks" +"use strict" -export const ruleName = "no-browser-hacks" +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const Result = require("postcss/lib/result") +const _ = require("lodash") +const stylehacks = require("stylehacks") -export const messages = ruleMessages(ruleName, { +const ruleName = "no-browser-hacks" + +const messages = ruleMessages(ruleName, { rejected: (type, hack) => `Unexpected ${type} hack "${hack}"`, }) -export default function (on, options) { +const rule = function (on, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: on }, { optional: true, actual: options, possible: { - browsers: [isString], + browsers: [_.isString], }, }) - if (!validOptions) { return } + if (!validOptions) { + return + } const stylehacksOptions = { lint: true } if (options && options.browsers) { @@ -44,3 +46,7 @@ export default function (on, options) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-descending-specificity/README.md b/lib/rules/no-descending-specificity/README.md similarity index 100% rename from src/rules/no-descending-specificity/README.md rename to lib/rules/no-descending-specificity/README.md diff --git a/src/rules/no-descending-specificity/__tests__/index.js b/lib/rules/no-descending-specificity/__tests__/index.js similarity index 93% rename from src/rules/no-descending-specificity/__tests__/index.js rename to lib/rules/no-descending-specificity/__tests__/index.js index 9dace9fad2..73e11a7c7e 100644 --- a/src/rules/no-descending-specificity/__tests__/index.js +++ b/lib/rules/no-descending-specificity/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/no-descending-specificity/index.js b/lib/rules/no-descending-specificity/index.js similarity index 62% rename from src/rules/no-descending-specificity/index.js rename to lib/rules/no-descending-specificity/index.js index 4086793f3f..952b17d49a 100644 --- a/src/rules/no-descending-specificity/index.js +++ b/lib/rules/no-descending-specificity/index.js @@ -1,43 +1,46 @@ -import { - calculate, - compare, -} from "specificity" -import { - findAtRuleContext, - isCustomPropertySet, - nodeContextLookup, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" -import { pseudoElements } from "../../reference/keywordSets" -import resolvedNestedSelector from "postcss-resolve-nested-selector" - -export const ruleName = "no-descending-specificity" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const specificity = require("specificity") +const findAtRuleContext = require("../../utils/findAtRuleContext") +const isCustomPropertySet = require("../../utils/isCustomPropertySet") +const nodeContextLookup = require("../../utils/nodeContextLookup") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const keywordSets = require("../../reference/keywordSets") +const resolvedNestedSelector = require("postcss-resolve-nested-selector") + +const ruleName = "no-descending-specificity" + +const messages = ruleMessages(ruleName, { rejected: (b, a) => `Expected selector "${b}" to come before selector "${a}"`, }) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } const selectorContextLookup = nodeContextLookup() root.walkRules(rule => { // Ignore custom property set `--foo: {};` - if (isCustomPropertySet(rule)) { return } + if (isCustomPropertySet(rule)) { + return + } const comparisonContext = selectorContextLookup.getContext(rule, findAtRuleContext(rule)) rule.selectors.forEach(selector => { const trimSelector = selector.trim() // Ignore `.selector, { }` - if (trimSelector === "") { return } + if (trimSelector === "") { + return + } // The edge-case of duplicate selectors will act acceptably const index = rule.selector.indexOf(trimSelector) @@ -51,7 +54,7 @@ export default function (actual) { function checkSelector(selectorNode, rule, sourceIndex, comparisonContext) { const selector = selectorNode.toString() const referenceSelectorNode = lastCompoundSelectorWithoutPseudoClasses(selectorNode) - const selectorSpecificity = calculate(selector)[0].specificityArray + const selectorSpecificity = specificity.calculate(selector)[0].specificityArray const entry = { selector, specificity: selectorSpecificity } if (!comparisonContext.has(referenceSelectorNode)) { @@ -62,7 +65,7 @@ export default function (actual) { const priorComparableSelectors = comparisonContext.get(referenceSelectorNode) priorComparableSelectors.forEach(priorEntry => { - if (compare(selectorSpecificity, priorEntry.specificity) === -1) { + if (specificity.compare(selectorSpecificity, priorEntry.specificity) === -1) { report({ ruleName, result, @@ -84,8 +87,12 @@ function lastCompoundSelectorWithoutPseudoClasses(selectorNode) { })) const nodesWithoutPseudoClasses = nodesAfterLastCombinator.filter(node => { - return node.type !== "pseudo" || pseudoElements.has(node.value.replace(/:/g, "")) + return node.type !== "pseudo" || keywordSets.pseudoElements.has(node.value.replace(/:/g, "")) }).join("") return nodesWithoutPseudoClasses.toString() } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-duplicate-selectors/README.md b/lib/rules/no-duplicate-selectors/README.md similarity index 100% rename from src/rules/no-duplicate-selectors/README.md rename to lib/rules/no-duplicate-selectors/README.md diff --git a/src/rules/no-duplicate-selectors/__tests__/fixtures/also-using-foo.css b/lib/rules/no-duplicate-selectors/__tests__/fixtures/also-using-foo.css similarity index 100% rename from src/rules/no-duplicate-selectors/__tests__/fixtures/also-using-foo.css rename to lib/rules/no-duplicate-selectors/__tests__/fixtures/also-using-foo.css diff --git a/src/rules/no-duplicate-selectors/__tests__/fixtures/using-foo-twice.css b/lib/rules/no-duplicate-selectors/__tests__/fixtures/using-foo-twice.css similarity index 100% rename from src/rules/no-duplicate-selectors/__tests__/fixtures/using-foo-twice.css rename to lib/rules/no-duplicate-selectors/__tests__/fixtures/using-foo-twice.css diff --git a/src/rules/no-duplicate-selectors/__tests__/fixtures/using-foo.css b/lib/rules/no-duplicate-selectors/__tests__/fixtures/using-foo.css similarity index 100% rename from src/rules/no-duplicate-selectors/__tests__/fixtures/using-foo.css rename to lib/rules/no-duplicate-selectors/__tests__/fixtures/using-foo.css diff --git a/src/rules/no-duplicate-selectors/__tests__/index.js b/lib/rules/no-duplicate-selectors/__tests__/index.js similarity index 73% rename from src/rules/no-duplicate-selectors/__tests__/index.js rename to lib/rules/no-duplicate-selectors/__tests__/index.js index fa45308f19..8be86fdd06 100644 --- a/src/rules/no-duplicate-selectors/__tests__/index.js +++ b/lib/rules/no-duplicate-selectors/__tests__/index.js @@ -1,13 +1,13 @@ -import { - messages, - ruleName, -} from ".." -import path from "path" -import postcss from "postcss" -import postcssImport from "postcss-import" -import rules from "../../../rules" -import test from "tape" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const path = require("path") +const postcss = require("postcss") +const postcssImport = require("postcss-import") +const rules = require("../../../rules") +const test = require("tape") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -91,24 +91,20 @@ testRule(rule, { test("with postcss-import and duplicates within a file", t => { t.plan(1) - postcss([ postcssImport(), rule() ]) - .process("@import 'fixtures/using-foo-twice.css';", { - from: path.join(__dirname, "test.css"), - }) - .then(result => { - const warnings = result.warnings() - t.equal(warnings.length, 1, "a warning strikes") - }) + postcss([ postcssImport(), rule() ]).process("@import 'fixtures/using-foo-twice.css';", { + from: path.join(__dirname, "test.css"), + }).then(result => { + const warnings = result.warnings() + t.equal(warnings.length, 1, "a warning strikes") + }) }) test("with postcss-import and duplicates across files", t => { t.plan(1) - postcss([ postcssImport(), rule() ]) - .process("@import 'fixtures/using-foo.css'; @import 'fixtures/also-using-foo.css';", { - from: path.join(__dirname, "test.css"), - }) - .then(result => { - const warnings = result.warnings() - t.equal(warnings.length, 0, "no warnings") - }) + postcss([ postcssImport(), rule() ]).process("@import 'fixtures/using-foo.css'; @import 'fixtures/also-using-foo.css';", { + from: path.join(__dirname, "test.css"), + }).then(result => { + const warnings = result.warnings() + t.equal(warnings.length, 0, "no warnings") + }) }) diff --git a/src/rules/no-duplicate-selectors/index.js b/lib/rules/no-duplicate-selectors/index.js similarity index 64% rename from src/rules/no-duplicate-selectors/index.js rename to lib/rules/no-duplicate-selectors/index.js index 468170aeb6..53d44ab3ca 100644 --- a/src/rules/no-duplicate-selectors/index.js +++ b/lib/rules/no-duplicate-selectors/index.js @@ -1,28 +1,27 @@ -import { - findAtRuleContext, - isKeyframeRule, - nodeContextLookup, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - includes, - union, -} from "lodash" -import normalizeSelector from "normalize-selector" -import resolvedNestedSelector from "postcss-resolve-nested-selector" +"use strict" -export const ruleName = "no-duplicate-selectors" +const findAtRuleContext = require("../../utils/findAtRuleContext") +const isKeyframeRule = require("../../utils/isKeyframeRule") +const nodeContextLookup = require("../../utils/nodeContextLookup") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const normalizeSelector = require("normalize-selector") +const resolvedNestedSelector = require("postcss-resolve-nested-selector") -export const messages = ruleMessages(ruleName, { +const ruleName = "no-duplicate-selectors" + +const messages = ruleMessages(ruleName, { rejected: selector => `Unexpected duplicate selector "${selector}"`, }) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } // The top level of this map will be rule sources. // Each source maps to another map, which maps rule parents to a set of selectors. @@ -31,11 +30,13 @@ export default function (actual) { const selectorContextLookup = nodeContextLookup() root.walkRules(rule => { - if (isKeyframeRule(rule)) { return } + if (isKeyframeRule(rule)) { + return + } const contextSelectorSet = selectorContextLookup.getContext(rule, findAtRuleContext(rule)) const resolvedSelectors = rule.selectors.reduce((result, selector) => { - return union(result, resolvedNestedSelector(selector, rule)) + return _.union(result, resolvedNestedSelector(selector, rule)) }, []) const normalizedSelectorList = resolvedSelectors.map(normalizeSelector) @@ -48,7 +49,7 @@ export default function (actual) { // If the selector isn't nested we can use its raw value; otherwise, // we have to approximate something for the message -- which is close enough const isNestedSelector = resolvedSelectors.join(",") !== rule.selectors.join(",") - const selectorForMessage = (isNestedSelector) ? resolvedSelectors.join(", ") : rule.selector + const selectorForMessage = isNestedSelector ? resolvedSelectors.join(", ") : rule.selector return report({ result, ruleName, @@ -62,7 +63,7 @@ export default function (actual) { // Or complain if one selector list contains the same selector more than one rule.selectors.forEach((selector, i) => { - if (includes(normalizedSelectorList.slice(0, i), normalizeSelector(selector))) { + if (_.includes(normalizedSelectorList.slice(0, i), normalizeSelector(selector))) { report({ result, ruleName, @@ -74,3 +75,7 @@ export default function (actual) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-empty-source/README.md b/lib/rules/no-empty-source/README.md similarity index 100% rename from src/rules/no-empty-source/README.md rename to lib/rules/no-empty-source/README.md diff --git a/src/rules/no-empty-source/__tests__/index.js b/lib/rules/no-empty-source/__tests__/index.js similarity index 88% rename from src/rules/no-empty-source/__tests__/index.js rename to lib/rules/no-empty-source/__tests__/index.js index 59ba2f40e8..c1ee79ccf4 100644 --- a/src/rules/no-empty-source/__tests__/index.js +++ b/lib/rules/no-empty-source/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/no-empty-source/index.js b/lib/rules/no-empty-source/index.js new file mode 100644 index 0000000000..400c69b8fc --- /dev/null +++ b/lib/rules/no-empty-source/index.js @@ -0,0 +1,35 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "no-empty-source" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected empty source", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + if (!/^\s*$/.test(root.toString())) { + return + } + + report({ + message: messages.rejected, + node: root, + result, + ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-eol-whitespace/README.md b/lib/rules/no-eol-whitespace/README.md similarity index 100% rename from src/rules/no-eol-whitespace/README.md rename to lib/rules/no-eol-whitespace/README.md diff --git a/src/rules/no-eol-whitespace/__tests__/index.js b/lib/rules/no-eol-whitespace/__tests__/index.js similarity index 97% rename from src/rules/no-eol-whitespace/__tests__/index.js rename to lib/rules/no-eol-whitespace/__tests__/index.js index b7bbd27683..6d48537dba 100644 --- a/src/rules/no-eol-whitespace/__tests__/index.js +++ b/lib/rules/no-eol-whitespace/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/no-eol-whitespace/index.js b/lib/rules/no-eol-whitespace/index.js similarity index 58% rename from src/rules/no-eol-whitespace/index.js rename to lib/rules/no-eol-whitespace/index.js index a552a8c005..a672c43cf8 100644 --- a/src/rules/no-eol-whitespace/index.js +++ b/lib/rules/no-eol-whitespace/index.js @@ -1,21 +1,21 @@ -import { - isOnlyWhitespace, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "no-eol-whitespace" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const isOnlyWhitespace = require("../../utils/isOnlyWhitespace") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") + +const ruleName = "no-eol-whitespace" + +const messages = ruleMessages(ruleName, { rejected: "Unexpected whitespace at end of line", }) const whitespacesToReject = new Set([ " ", "\t" ]) -export default function (on, options) { +const rule = function (on, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: on, @@ -26,7 +26,9 @@ export default function (on, options) { ignore: ["empty-lines"], }, }) - if (!validOptions) { return } + if (!validOptions) { + return + } const rootString = root.toString() styleSearch({ @@ -35,14 +37,17 @@ export default function (on, options) { comments: "check", }, match => { // If the character before newline is not whitespace, ignore - if (!whitespacesToReject.has(rootString[match.startIndex - 1])) { return } + if (!whitespacesToReject.has(rootString[match.startIndex - 1])) { + return + } if (optionsMatches(options, "ignore", "empty-lines")) { // If there is only whitespace between the previous newline and // this newline, ignore - const lineBefore = rootString.substring(match.startIndex + 1, - rootString.lastIndexOf("\n", match.startIndex - 1)) - if (isOnlyWhitespace(lineBefore)) { return } + const lineBefore = rootString.substring(match.startIndex + 1, rootString.lastIndexOf("\n", match.startIndex - 1)) + if (isOnlyWhitespace(lineBefore)) { + return + } } report({ @@ -55,3 +60,7 @@ export default function (on, options) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-extra-semicolons/README.md b/lib/rules/no-extra-semicolons/README.md similarity index 100% rename from src/rules/no-extra-semicolons/README.md rename to lib/rules/no-extra-semicolons/README.md diff --git a/src/rules/no-extra-semicolons/__tests__/index.js b/lib/rules/no-extra-semicolons/__tests__/index.js similarity index 98% rename from src/rules/no-extra-semicolons/__tests__/index.js rename to lib/rules/no-extra-semicolons/__tests__/index.js index 2d94263b9c..bb165a8bd9 100644 --- a/src/rules/no-extra-semicolons/__tests__/index.js +++ b/lib/rules/no-extra-semicolons/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/no-extra-semicolons/index.js b/lib/rules/no-extra-semicolons/index.js similarity index 62% rename from src/rules/no-extra-semicolons/index.js rename to lib/rules/no-extra-semicolons/index.js index 202e7945d9..da7b52c27d 100644 --- a/src/rules/no-extra-semicolons/index.js +++ b/lib/rules/no-extra-semicolons/index.js @@ -1,15 +1,15 @@ -import { - hasEmptyBlock, - isCustomPropertySet, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "no-extra-semicolons" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const hasEmptyBlock = require("../../utils/hasEmptyBlock") +const isCustomPropertySet = require("../../utils/isCustomPropertySet") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") + +const ruleName = "no-extra-semicolons" + +const messages = ruleMessages(ruleName, { rejected: "Unexpected extra semicolon", }) @@ -38,30 +38,36 @@ function getOffsetByNode(node) { return index } -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } const rawAfterRoot = root.raws.after if (rawAfterRoot && rawAfterRoot.trim().length !== 0) { - styleSearch({ source: rawAfterRoot, target: ";" }, (match) => { + styleSearch({ source: rawAfterRoot, target: ";" }, match => { complain(root.toString().length - rawAfterRoot.length + match.startIndex) }) } - root.walk((node) => { + root.walk(node => { const rawBeforeNode = node.raws.before if (rawBeforeNode && rawBeforeNode.trim().length !== 0) { let allowedSemi = 0 // Forbid semicolon before first custom properties sets - if (isCustomPropertySet(node) && node.parent.index(node) > 0) { allowedSemi = 1 } + if (isCustomPropertySet(node) && node.parent.index(node) > 0) { + allowedSemi = 1 + } styleSearch({ source: rawBeforeNode, target: ";" }, (match, count) => { - if (count === allowedSemi) { return } + if (count === allowedSemi) { + return + } complain(getOffsetByNode(node) - rawBeforeNode.length + match.startIndex) }) @@ -72,15 +78,16 @@ export default function (actual) { if (rawAfterNode && rawAfterNode.trim().length !== 0) { let allowedSemi = 0 - if (!hasEmptyBlock(node) && isCustomPropertySet(node.nodes[node.nodes.length - 1])) { allowedSemi = 1 } + if (!hasEmptyBlock(node) && isCustomPropertySet(node.nodes[node.nodes.length - 1])) { + allowedSemi = 1 + } styleSearch({ source: rawAfterNode, target: ";" }, (match, count) => { - if (count === allowedSemi) { return } + if (count === allowedSemi) { + return + } - const index = getOffsetByNode(node) - + node.toString().length - 1 - - rawAfterNode.length - + match.startIndex + const index = getOffsetByNode(node) + node.toString().length - 1 - rawAfterNode.length + match.startIndex complain(index) }) } @@ -97,3 +104,7 @@ export default function (actual) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-indistinguishable-colors/README.md b/lib/rules/no-indistinguishable-colors/README.md similarity index 100% rename from src/rules/no-indistinguishable-colors/README.md rename to lib/rules/no-indistinguishable-colors/README.md diff --git a/src/rules/no-indistinguishable-colors/__tests__/index.js b/lib/rules/no-indistinguishable-colors/__tests__/index.js similarity index 90% rename from src/rules/no-indistinguishable-colors/__tests__/index.js rename to lib/rules/no-indistinguishable-colors/__tests__/index.js index 0f5a8718e8..a5cb47b8ff 100644 --- a/src/rules/no-indistinguishable-colors/__tests__/index.js +++ b/lib/rules/no-indistinguishable-colors/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/no-indistinguishable-colors/index.js b/lib/rules/no-indistinguishable-colors/index.js similarity index 51% rename from src/rules/no-indistinguishable-colors/index.js rename to lib/rules/no-indistinguishable-colors/index.js index ba9f4605e8..1e24398073 100644 --- a/src/rules/no-indistinguishable-colors/index.js +++ b/lib/rules/no-indistinguishable-colors/index.js @@ -1,34 +1,33 @@ -import { - isArray, - isNumber, -} from "lodash" -import { - isValidHex, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import Result from "postcss/lib/result" -import colorguard from "colorguard" +"use strict" -export const ruleName = "no-indistinguishable-colors" +const _ = require("lodash") +const isValidHex = require("../../utils/isValidHex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const Result = require("postcss/lib/result") +const colorguard = require("colorguard") -export const messages = ruleMessages(ruleName, { +const ruleName = "no-indistinguishable-colors" + +const messages = ruleMessages(ruleName, { rejected: (a, b) => `Unexpected indistinguishable colors "${a}" and "${b}"`, }) -export default function (on, options) { +const rule = function (on, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: on }, { optional: true, actual: options, possible: { ignore: isValidHex, - threshold: x => isNumber(x) && x >= 0 && x <= 100, - whitelist: x => isArray(x) && x.every(isValidHex), + threshold: x => _.isNumber(x) && x >= 0 && x <= 100, + whitelist: x => _.isArray(x) && x.every(isValidHex), }, }) - if (!validOptions) { return } + if (!validOptions) { + return + } const colorguardResult = new Result() colorguard(options)(root, colorguardResult) @@ -44,3 +43,7 @@ export default function (on, options) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-invalid-double-slash-comments/README.md b/lib/rules/no-invalid-double-slash-comments/README.md similarity index 100% rename from src/rules/no-invalid-double-slash-comments/README.md rename to lib/rules/no-invalid-double-slash-comments/README.md diff --git a/src/rules/no-invalid-double-slash-comments/__tests__/index.js b/lib/rules/no-invalid-double-slash-comments/__tests__/index.js similarity index 90% rename from src/rules/no-invalid-double-slash-comments/__tests__/index.js rename to lib/rules/no-invalid-double-slash-comments/__tests__/index.js index adb3be320c..002e85329e 100644 --- a/src/rules/no-invalid-double-slash-comments/__tests__/index.js +++ b/lib/rules/no-invalid-double-slash-comments/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/no-invalid-double-slash-comments/index.js b/lib/rules/no-invalid-double-slash-comments/index.js similarity index 60% rename from src/rules/no-invalid-double-slash-comments/index.js rename to lib/rules/no-invalid-double-slash-comments/index.js index 5ce4d89e45..f51e26ef2a 100644 --- a/src/rules/no-invalid-double-slash-comments/index.js +++ b/lib/rules/no-invalid-double-slash-comments/index.js @@ -1,19 +1,21 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" +"use strict" -export const ruleName = "no-invalid-double-slash-comments" +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") -export const messages = ruleMessages(ruleName, { +const ruleName = "no-invalid-double-slash-comments" + +const messages = ruleMessages(ruleName, { rejected: "Unexpected double-slash CSS comment", }) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { if (decl.prop.indexOf("//") === 0) { @@ -39,3 +41,7 @@ export default function (actual) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-missing-end-of-source-newline/README.md b/lib/rules/no-missing-end-of-source-newline/README.md similarity index 100% rename from src/rules/no-missing-end-of-source-newline/README.md rename to lib/rules/no-missing-end-of-source-newline/README.md diff --git a/src/rules/no-missing-end-of-source-newline/__tests__/index.js b/lib/rules/no-missing-end-of-source-newline/__tests__/index.js similarity index 81% rename from src/rules/no-missing-end-of-source-newline/__tests__/index.js rename to lib/rules/no-missing-end-of-source-newline/__tests__/index.js index 10de165583..36ee50aa74 100644 --- a/src/rules/no-missing-end-of-source-newline/__tests__/index.js +++ b/lib/rules/no-missing-end-of-source-newline/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/no-missing-end-of-source-newline/index.js b/lib/rules/no-missing-end-of-source-newline/index.js new file mode 100644 index 0000000000..64287c0a18 --- /dev/null +++ b/lib/rules/no-missing-end-of-source-newline/index.js @@ -0,0 +1,37 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "no-missing-end-of-source-newline" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected missing end-of-source newline", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + const sourceCss = root.source.input.css + if (sourceCss === "" || sourceCss.slice(-1) === "\n") { + return + } + + report({ + message: messages.rejected, + node: root, + index: sourceCss.length - 1, + result, + ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-unknown-animations/README.md b/lib/rules/no-unknown-animations/README.md similarity index 100% rename from src/rules/no-unknown-animations/README.md rename to lib/rules/no-unknown-animations/README.md diff --git a/src/rules/no-unknown-animations/__tests__/index.js b/lib/rules/no-unknown-animations/__tests__/index.js similarity index 97% rename from src/rules/no-unknown-animations/__tests__/index.js rename to lib/rules/no-unknown-animations/__tests__/index.js index a3d5322f24..579163f7d5 100644 --- a/src/rules/no-unknown-animations/__tests__/index.js +++ b/lib/rules/no-unknown-animations/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/no-unknown-animations/index.js b/lib/rules/no-unknown-animations/index.js new file mode 100644 index 0000000000..3e910960a4 --- /dev/null +++ b/lib/rules/no-unknown-animations/index.js @@ -0,0 +1,59 @@ +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const findAnimationName = require("../../utils/findAnimationName") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") + +const ruleName = "no-unknown-animations" + +const messages = ruleMessages(ruleName, { + rejected: animationName => `Unexpected unknown animation name "${animationName}"`, +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + const declaredAnimations = new Set() + root.walkAtRules(/(-(moz|webkit)-)?keyframes/i, atRule => { + declaredAnimations.add(atRule.params) + }) + + root.walkDecls(decl => { + if (decl.prop.toLowerCase() === "animation" || decl.prop.toLowerCase() === "animation-name") { + const animationNames = findAnimationName(decl.value) + + if (animationNames.length === 0) { + return + } + + animationNames.forEach(animationNameNode => { + if (keywordSets.animationNameKeywords.has(animationNameNode.value.toLowerCase())) { + return + } + if (declaredAnimations.has(animationNameNode.value)) { + return + } + + report({ + result, + ruleName, + message: messages.rejected(animationNameNode.value), + node: decl, + index: declarationValueIndex(decl) + animationNameNode.sourceIndex, + }) + }) + } + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/no-unsupported-browser-features/README.md b/lib/rules/no-unsupported-browser-features/README.md similarity index 100% rename from src/rules/no-unsupported-browser-features/README.md rename to lib/rules/no-unsupported-browser-features/README.md diff --git a/src/rules/no-unsupported-browser-features/__tests__/index.js b/lib/rules/no-unsupported-browser-features/__tests__/index.js similarity index 89% rename from src/rules/no-unsupported-browser-features/__tests__/index.js rename to lib/rules/no-unsupported-browser-features/__tests__/index.js index 18f2a491d7..ad8fef156b 100644 --- a/src/rules/no-unsupported-browser-features/__tests__/index.js +++ b/lib/rules/no-unsupported-browser-features/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/no-unsupported-browser-features/index.js b/lib/rules/no-unsupported-browser-features/index.js similarity index 74% rename from src/rules/no-unsupported-browser-features/index.js rename to lib/rules/no-unsupported-browser-features/index.js index f963ec4121..b348b99dff 100644 --- a/src/rules/no-unsupported-browser-features/index.js +++ b/lib/rules/no-unsupported-browser-features/index.js @@ -1,29 +1,31 @@ +"use strict" + const doiuse = require("doiuse") -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import Result from "postcss/lib/result" -import { isString } from "lodash" +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const Result = require("postcss/lib/result") +const _ = require("lodash") -export const ruleName = "no-unsupported-browser-features" +const ruleName = "no-unsupported-browser-features" -export const messages = ruleMessages(ruleName, { +const messages = ruleMessages(ruleName, { rejected: details => `Unexpected browser feature ${details}`, }) -export default function (on, options) { +const rule = function (on, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: on }, { optional: true, actual: options, possible: { - browsers: [isString], - ignore: [isString], + browsers: [_.isString], + ignore: [_.isString], }, }) - if (!validOptions) { return } + if (!validOptions) { + return + } const doiuseOptions = {} @@ -65,3 +67,7 @@ function cleanDoiuseWarningText(warningText) { return `${featureId} is ${browserSupport}` } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/number-leading-zero/README.md b/lib/rules/number-leading-zero/README.md similarity index 100% rename from src/rules/number-leading-zero/README.md rename to lib/rules/number-leading-zero/README.md diff --git a/src/rules/number-leading-zero/__tests__/index.js b/lib/rules/number-leading-zero/__tests__/index.js similarity index 97% rename from src/rules/number-leading-zero/__tests__/index.js rename to lib/rules/number-leading-zero/__tests__/index.js index 49f1ce1a27..6b0b00e6cd 100644 --- a/src/rules/number-leading-zero/__tests__/index.js +++ b/lib/rules/number-leading-zero/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/number-leading-zero/index.js b/lib/rules/number-leading-zero/index.js similarity index 59% rename from src/rules/number-leading-zero/index.js rename to lib/rules/number-leading-zero/index.js index 097116bc0d..5a1be1f9f7 100644 --- a/src/rules/number-leading-zero/index.js +++ b/lib/rules/number-leading-zero/index.js @@ -1,56 +1,63 @@ -import { - atRuleParamIndex, - declarationValueIndex, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import valueParser from "postcss-value-parser" - -export const ruleName = "number-leading-zero" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const declarationValueIndex = require("../../utils/declarationValueIndex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const valueParser = require("postcss-value-parser") + +const ruleName = "number-leading-zero" + +const messages = ruleMessages(ruleName, { expected: "Expected a leading zero", rejected: "Unexpected leading zero", }) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - ], + possible: [ "always", "never" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkAtRules(atRule => { - if (atRule.name.toLowerCase() === "import") { return } + if (atRule.name.toLowerCase() === "import") { + return + } check(atRule, atRule.params, atRuleParamIndex) }) - root.walkDecls(decl => - check(decl, decl.value, declarationValueIndex) - ) + root.walkDecls(decl => check(decl, decl.value, declarationValueIndex)) function check(node, value, getIndex) { // Get out quickly if there are no periods - if (value.indexOf(".") === -1) { return } + if (value.indexOf(".") === -1) { + return + } valueParser(value).walk(valueNode => { // Ignore `url` function - if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { return false } + if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { + return false + } // Ignore strings, comments, etc - if (valueNode.type !== "word") { return } + if (valueNode.type !== "word") { + return + } // Check leading zero if (expectation === "always") { const match = /(?:\D|^)(\.\d+)/.exec(valueNode.value) - if (match === null) { return } + if (match === null) { + return + } // The regexp above consists of 2 capturing groups (or capturing parentheses). // We need the index of the second group. This makes sanse when we have "-.5" as an input @@ -62,7 +69,9 @@ export default function (expectation) { if (expectation === "never") { const match = /(?:\D|^)(0+\.\d+)/.exec(valueNode.value) - if (match === null) { return } + if (match === null) { + return + } const capturingGroupIndex = match[0].length - match[1].length complain(messages.rejected, node, getIndex(node) + valueNode.sourceIndex + match.index + capturingGroupIndex) @@ -81,3 +90,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/number-max-precision/README.md b/lib/rules/number-max-precision/README.md similarity index 100% rename from src/rules/number-max-precision/README.md rename to lib/rules/number-max-precision/README.md diff --git a/src/rules/number-max-precision/__tests__/index.js b/lib/rules/number-max-precision/__tests__/index.js similarity index 92% rename from src/rules/number-max-precision/__tests__/index.js rename to lib/rules/number-max-precision/__tests__/index.js index 71758314b4..523b4519ed 100644 --- a/src/rules/number-max-precision/__tests__/index.js +++ b/lib/rules/number-max-precision/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/number-max-precision/index.js b/lib/rules/number-max-precision/index.js new file mode 100644 index 0000000000..0ea5f91abf --- /dev/null +++ b/lib/rules/number-max-precision/index.js @@ -0,0 +1,79 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const declarationValueIndex = require("../../utils/declarationValueIndex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const _ = require("lodash") +const valueParser = require("postcss-value-parser") + +const ruleName = "number-max-precision" + +const messages = ruleMessages(ruleName, { + expected: (number, precision) => `Expected "${number}" to be "${number.toFixed(precision)}"`, +}) + +const rule = function (precision) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: precision, + possible: [_.isNumber], + }) + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + if (atRule.name.toLowerCase() === "import") { + return + } + + check(atRule, atRule.params, atRuleParamIndex) + }) + + root.walkDecls(decl => check(decl, decl.value, declarationValueIndex)) + + function check(node, value, getIndex) { + // Get out quickly if there are no periods + if (value.indexOf(".") === -1) { + return + } + + valueParser(value).walk(valueNode => { + // Ignore `url` function + if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { + return false + } + + // Ignore strings, comments, etc + if (valueNode.type !== "word") { + return + } + + const match = /\d*\.(\d+)/.exec(valueNode.value) + + if (match === null) { + return + } + + if (match[1].length <= precision) { + return + } + + report({ + result, + ruleName, + node, + index: getIndex(node) + valueNode.sourceIndex + match.index, + message: messages.expected(parseFloat(match[0]), precision), + }) + }) + } + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/number-no-trailing-zeros/README.md b/lib/rules/number-no-trailing-zeros/README.md similarity index 100% rename from src/rules/number-no-trailing-zeros/README.md rename to lib/rules/number-no-trailing-zeros/README.md diff --git a/src/rules/number-no-trailing-zeros/__tests__/index.js b/lib/rules/number-no-trailing-zeros/__tests__/index.js similarity index 92% rename from src/rules/number-no-trailing-zeros/__tests__/index.js rename to lib/rules/number-no-trailing-zeros/__tests__/index.js index b4e84de18a..664d3992cd 100644 --- a/src/rules/number-no-trailing-zeros/__tests__/index.js +++ b/lib/rules/number-no-trailing-zeros/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/number-no-trailing-zeros/index.js b/lib/rules/number-no-trailing-zeros/index.js new file mode 100644 index 0000000000..0fe8184d3a --- /dev/null +++ b/lib/rules/number-no-trailing-zeros/index.js @@ -0,0 +1,70 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const declarationValueIndex = require("../../utils/declarationValueIndex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const valueParser = require("postcss-value-parser") + +const ruleName = "number-no-trailing-zeros" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected trailing zero(s)", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkAtRules(atRule => { + if (atRule.name.toLowerCase() === "import") { + return + } + + check(atRule, atRule.params, atRuleParamIndex) + }) + + root.walkDecls(decl => check(decl, decl.value, declarationValueIndex)) + + function check(node, value, getIndex) { + // Get out quickly if there are no periods + if (value.indexOf(".") === -1) { + return + } + + valueParser(value).walk(valueNode => { + // Ignore `url` function + if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { + return false + } + + // Ignore strings, comments, etc + if (valueNode.type !== "word") { + return + } + + const match = /(\.\d*)0+(?:\D|$)/.exec(valueNode.value) + + if (match === null) { + return + } + + report({ + message: messages.rejected, + node, + index: getIndex(node) + valueNode.sourceIndex + match.index + match[1].length, + result, + ruleName, + }) + }) + } + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/property-blacklist/README.md b/lib/rules/property-blacklist/README.md similarity index 100% rename from src/rules/property-blacklist/README.md rename to lib/rules/property-blacklist/README.md diff --git a/src/rules/property-blacklist/__tests__/index.js b/lib/rules/property-blacklist/__tests__/index.js similarity index 85% rename from src/rules/property-blacklist/__tests__/index.js rename to lib/rules/property-blacklist/__tests__/index.js index 497698b3dc..61fbec1578 100644 --- a/src/rules/property-blacklist/__tests__/index.js +++ b/lib/rules/property-blacklist/__tests__/index.js @@ -1,19 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "transform", - "background-size", - ]], + config: [[ "transform", "background-size" ]], accept: [ { code: "a { color: pink; }", @@ -50,9 +47,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "/^background/", - ]], + config: [["/^background/"]], accept: [ { code: "a { color: pink; }", diff --git a/lib/rules/property-blacklist/index.js b/lib/rules/property-blacklist/index.js new file mode 100644 index 0000000000..3b752064b0 --- /dev/null +++ b/lib/rules/property-blacklist/index.js @@ -0,0 +1,55 @@ +"use strict" + +const isCustomProperty = require("../../utils/isCustomProperty") +const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "property-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: property => `Unexpected property "${property}"`, +}) + +const rule = function (blacklist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop + + if (!isStandardSyntaxProperty(prop)) { + return + } + if (isCustomProperty(prop)) { + return + } + if (!matchesStringOrRegExp(postcss.vendor.unprefixed(prop), blacklist)) { + return + } + + report({ + message: messages.rejected(prop), + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/property-case/README.md b/lib/rules/property-case/README.md similarity index 100% rename from src/rules/property-case/README.md rename to lib/rules/property-case/README.md diff --git a/src/rules/property-case/__tests__/index.js b/lib/rules/property-case/__tests__/index.js similarity index 98% rename from src/rules/property-case/__tests__/index.js rename to lib/rules/property-case/__tests__/index.js index a85ea2917e..7863e91110 100644 --- a/src/rules/property-case/__tests__/index.js +++ b/lib/rules/property-case/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/property-case/index.js b/lib/rules/property-case/index.js new file mode 100644 index 0000000000..a0b2da87c4 --- /dev/null +++ b/lib/rules/property-case/index.js @@ -0,0 +1,52 @@ +"use strict" + +const isCustomProperty = require("../../utils/isCustomProperty") +const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "property-case" + +const messages = ruleMessages(ruleName, { + expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "lower", "upper" ], + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop + + if (!isStandardSyntaxProperty(prop)) { + return + } + if (isCustomProperty(prop)) { + return + } + + const expectedProp = expectation === "lower" ? prop.toLowerCase() : prop.toUpperCase() + if (prop === expectedProp) { + return + } + + report({ + message: messages.expected(prop, expectedProp), + node: decl, + ruleName, + result, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/property-no-unknown/README.md b/lib/rules/property-no-unknown/README.md similarity index 100% rename from src/rules/property-no-unknown/README.md rename to lib/rules/property-no-unknown/README.md diff --git a/src/rules/property-no-unknown/__tests__/index.js b/lib/rules/property-no-unknown/__tests__/index.js similarity index 94% rename from src/rules/property-no-unknown/__tests__/index.js rename to lib/rules/property-no-unknown/__tests__/index.js index 43fa6cdaf2..6a219baf28 100644 --- a/src/rules/property-no-unknown/__tests__/index.js +++ b/lib/rules/property-no-unknown/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/property-no-unknown/index.js b/lib/rules/property-no-unknown/index.js new file mode 100644 index 0000000000..3f07ff02b1 --- /dev/null +++ b/lib/rules/property-no-unknown/index.js @@ -0,0 +1,74 @@ +"use strict" + +const isCustomProperty = require("../../utils/isCustomProperty") +const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration") +const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const properties = require("known-css-properties").all +const postcss = require("postcss") + +const ruleName = "property-no-unknown" + +const messages = ruleMessages(ruleName, { + rejected: property => `Unexpected unknown property "${property}"`, +}) + +const rule = function (actual, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }, { + actual: options, + possible: { + ignoreProperties: [_.isString], + checkPrefixed: _.isBoolean, + }, + optional: true, + }) + + if (!validOptions) { + return + } + + const shouldCheckPrefixed = _.get(options, "checkPrefixed") + + root.walkDecls(decl => { + const prop = decl.prop + + if (!isStandardSyntaxProperty(prop)) { + return + } + if (!isStandardSyntaxDeclaration(decl)) { + return + } + if (isCustomProperty(prop)) { + return + } + + if (!shouldCheckPrefixed && postcss.vendor.prefix(prop)) { + return + } + + if (optionsMatches(options, "ignoreProperties", prop)) { + return + } + + if (properties.indexOf(prop.toLowerCase()) !== -1) { + return + } + + report({ + message: messages.rejected(prop), + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/property-no-vendor-prefix/README.md b/lib/rules/property-no-vendor-prefix/README.md similarity index 100% rename from src/rules/property-no-vendor-prefix/README.md rename to lib/rules/property-no-vendor-prefix/README.md diff --git a/src/rules/property-no-vendor-prefix/__tests__/index.js b/lib/rules/property-no-vendor-prefix/__tests__/index.js similarity index 91% rename from src/rules/property-no-vendor-prefix/__tests__/index.js rename to lib/rules/property-no-vendor-prefix/__tests__/index.js index d348930663..8d1bb22a6b 100644 --- a/src/rules/property-no-vendor-prefix/__tests__/index.js +++ b/lib/rules/property-no-vendor-prefix/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/property-no-vendor-prefix/index.js b/lib/rules/property-no-vendor-prefix/index.js new file mode 100644 index 0000000000..57febcfc6c --- /dev/null +++ b/lib/rules/property-no-vendor-prefix/index.js @@ -0,0 +1,46 @@ +"use strict" + +const isAutoprefixable = require("../../utils/isAutoprefixable") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "property-no-vendor-prefix" + +const messages = ruleMessages(ruleName, { + rejected: property => `Unexpected vendor-prefix "${property}"`, +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop + + // Make sure there's a vendor prefix, + // but this isn't a custom property + + if (prop[0] !== "-" || prop[1] === "-") { + return + } + + if (!isAutoprefixable.property(prop)) { + return + } + report({ + message: messages.rejected(prop), + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/property-whitelist/README.md b/lib/rules/property-whitelist/README.md similarity index 100% rename from src/rules/property-whitelist/README.md rename to lib/rules/property-whitelist/README.md diff --git a/src/rules/property-whitelist/__tests__/index.js b/lib/rules/property-whitelist/__tests__/index.js similarity index 86% rename from src/rules/property-whitelist/__tests__/index.js rename to lib/rules/property-whitelist/__tests__/index.js index 06fbdbbf7c..1c4a7c050a 100644 --- a/src/rules/property-whitelist/__tests__/index.js +++ b/lib/rules/property-whitelist/__tests__/index.js @@ -1,19 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "transform", - "background-size", - ]], + config: [[ "transform", "background-size" ]], accept: [ { code: "a { background-size: cover; }", @@ -59,9 +56,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "/^background/", - ]], + config: [["/^background/"]], accept: [ { code: "a { background: pink; }", diff --git a/lib/rules/property-whitelist/index.js b/lib/rules/property-whitelist/index.js new file mode 100644 index 0000000000..ea3472536c --- /dev/null +++ b/lib/rules/property-whitelist/index.js @@ -0,0 +1,55 @@ +"use strict" + +const isCustomProperty = require("../../utils/isCustomProperty") +const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "property-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: property => `Unexpected property "${property}"`, +}) + +const rule = function (whitelist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop + + if (!isStandardSyntaxProperty(prop)) { + return + } + if (isCustomProperty(prop)) { + return + } + if (matchesStringOrRegExp(postcss.vendor.unprefixed(prop), whitelist)) { + return + } + + report({ + message: messages.rejected(prop), + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/root-no-standard-properties/README.md b/lib/rules/root-no-standard-properties/README.md similarity index 100% rename from src/rules/root-no-standard-properties/README.md rename to lib/rules/root-no-standard-properties/README.md diff --git a/src/rules/root-no-standard-properties/__tests__/index.js b/lib/rules/root-no-standard-properties/__tests__/index.js similarity index 93% rename from src/rules/root-no-standard-properties/__tests__/index.js rename to lib/rules/root-no-standard-properties/__tests__/index.js index a9aa15a6ff..1553236d67 100644 --- a/src/rules/root-no-standard-properties/__tests__/index.js +++ b/lib/rules/root-no-standard-properties/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/root-no-standard-properties/index.js b/lib/rules/root-no-standard-properties/index.js new file mode 100644 index 0000000000..14aa709064 --- /dev/null +++ b/lib/rules/root-no-standard-properties/index.js @@ -0,0 +1,73 @@ +"use strict" + +const isCustomProperty = require("../../utils/isCustomProperty") +const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "root-no-standard-properties" + +const messages = ruleMessages(ruleName, { + rejected: property => `Unexpected standard property "${property}"`, +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (rule.selector.toLowerCase().indexOf(":root") === -1) { + return + } + parseSelector(rule.selector, result, rule, checkSelector) + + function checkSelector(selectorAST) { + if (ignoreRule(selectorAST)) { + return + } + + rule.each(function (node) { + if (node.type !== "decl") { + return + } + + const prop = node.prop + + if (!isStandardSyntaxProperty(prop)) { + return + } + if (isCustomProperty(prop)) { + return + } + + report({ + message: messages.rejected(prop), + node, + result, + ruleName, + }) + }) + } + }) + } +} + +function ignoreRule(selectorAST) { + let ignore = false + selectorAST.walk(selectorNode => { + // ignore `:root` selector inside a `:not()` selector + if (selectorNode.value && selectorNode.value.toLowerCase() === ":root" && selectorNode.parent.parent.value && selectorNode.parent.parent.value.toLowerCase() === ":not") { + ignore = true + } + }) + return ignore +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/rule-nested-empty-line-before/README.md b/lib/rules/rule-nested-empty-line-before/README.md similarity index 100% rename from src/rules/rule-nested-empty-line-before/README.md rename to lib/rules/rule-nested-empty-line-before/README.md diff --git a/src/rules/rule-nested-empty-line-before/__tests__/index.js b/lib/rules/rule-nested-empty-line-before/__tests__/index.js similarity index 98% rename from src/rules/rule-nested-empty-line-before/__tests__/index.js rename to lib/rules/rule-nested-empty-line-before/__tests__/index.js index 74c3eabc23..de651d8d84 100644 --- a/src/rules/rule-nested-empty-line-before/__tests__/index.js +++ b/lib/rules/rule-nested-empty-line-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/rule-nested-empty-line-before/index.js b/lib/rules/rule-nested-empty-line-before/index.js new file mode 100644 index 0000000000..727f8227d8 --- /dev/null +++ b/lib/rules/rule-nested-empty-line-before/index.js @@ -0,0 +1,49 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const checkRuleEmptyLineBefore = require("../checkRuleEmptyLineBefore") + +const ruleName = "rule-nested-empty-line-before" + +const messages = ruleMessages(ruleName, { + expected: "Expected empty line before nested rule", + rejected: "Unexpected empty line before nested rule", +}) + +const rule = function (expectation, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never", "always-multi-line", "never-multi-line" ], + }, { + actual: options, + possible: { + ignore: ["after-comment"], + except: [ "first-nested", "after-comment" ], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + + // Only attend to nested rule sets + if (rule.parent === root) { + return + } + + checkRuleEmptyLineBefore({ rule, expectation, options, result, messages, checkedRuleName: ruleName }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/rule-non-nested-empty-line-before/README.md b/lib/rules/rule-non-nested-empty-line-before/README.md similarity index 100% rename from src/rules/rule-non-nested-empty-line-before/README.md rename to lib/rules/rule-non-nested-empty-line-before/README.md diff --git a/src/rules/rule-non-nested-empty-line-before/__tests__/index.js b/lib/rules/rule-non-nested-empty-line-before/__tests__/index.js similarity index 96% rename from src/rules/rule-non-nested-empty-line-before/__tests__/index.js rename to lib/rules/rule-non-nested-empty-line-before/__tests__/index.js index eb2b67414a..2eb243a9b2 100644 --- a/src/rules/rule-non-nested-empty-line-before/__tests__/index.js +++ b/lib/rules/rule-non-nested-empty-line-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/rule-non-nested-empty-line-before/index.js b/lib/rules/rule-non-nested-empty-line-before/index.js new file mode 100644 index 0000000000..f60c224788 --- /dev/null +++ b/lib/rules/rule-non-nested-empty-line-before/index.js @@ -0,0 +1,54 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const checkRuleEmptyLineBefore = require("../checkRuleEmptyLineBefore") + +const ruleName = "rule-non-nested-empty-line-before" + +const messages = ruleMessages(ruleName, { + expected: "Expected empty line before non-nested rule", + rejected: "Unexpected empty line before non-nested rule", +}) + +const rule = function (expectation, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never", "always-multi-line", "never-multi-line" ], + }, { + actual: options, + possible: { + ignore: ["after-comment"], + except: ["after-single-line-comment"], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + + // Ignore nested rule sets + if (rule.parent !== root) { + return + } + + // Ignore the first node + if (rule === root.first) { + return + } + + checkRuleEmptyLineBefore({ rule, expectation, options, result, messages, checkedRuleName: ruleName }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-attribute-brackets-space-inside/README.md b/lib/rules/selector-attribute-brackets-space-inside/README.md similarity index 100% rename from src/rules/selector-attribute-brackets-space-inside/README.md rename to lib/rules/selector-attribute-brackets-space-inside/README.md diff --git a/src/rules/selector-attribute-brackets-space-inside/__tests__/index.js b/lib/rules/selector-attribute-brackets-space-inside/__tests__/index.js similarity index 98% rename from src/rules/selector-attribute-brackets-space-inside/__tests__/index.js rename to lib/rules/selector-attribute-brackets-space-inside/__tests__/index.js index 7cdc5f59a2..6c51ad0fa9 100644 --- a/src/rules/selector-attribute-brackets-space-inside/__tests__/index.js +++ b/lib/rules/selector-attribute-brackets-space-inside/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/selector-attribute-brackets-space-inside/index.js b/lib/rules/selector-attribute-brackets-space-inside/index.js similarity index 70% rename from src/rules/selector-attribute-brackets-space-inside/index.js rename to lib/rules/selector-attribute-brackets-space-inside/index.js index 96440ba66e..7a7bf6003e 100644 --- a/src/rules/selector-attribute-brackets-space-inside/index.js +++ b/lib/rules/selector-attribute-brackets-space-inside/index.js @@ -1,35 +1,38 @@ -import { - isStandardSyntaxRule, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" +"use strict" -export const ruleName = "selector-attribute-brackets-space-inside" +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "selector-attribute-brackets-space-inside" + +const messages = ruleMessages(ruleName, { expectedOpening: "Expected single space after \"[\"", rejectedOpening: "Unexpected whitespace after \"[\"", expectedClosing: "Expected single space before \"]\"", rejectedClosing: "Unexpected whitespace before \"]\"", }) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - ], + possible: [ "always", "never" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (rule.selector.indexOf("[") === -1) { return } + if (!isStandardSyntaxRule(rule)) { + return + } + if (rule.selector.indexOf("[") === -1) { + return + } parseSelector(rule.selector, result, rule, selectorTree => { selectorTree.walkAttributes(attributeNode => { @@ -71,3 +74,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-attribute-operator-blacklist/README.md b/lib/rules/selector-attribute-operator-blacklist/README.md similarity index 100% rename from src/rules/selector-attribute-operator-blacklist/README.md rename to lib/rules/selector-attribute-operator-blacklist/README.md diff --git a/src/rules/selector-attribute-operator-blacklist/__tests__/index.js b/lib/rules/selector-attribute-operator-blacklist/__tests__/index.js similarity index 81% rename from src/rules/selector-attribute-operator-blacklist/__tests__/index.js rename to lib/rules/selector-attribute-operator-blacklist/__tests__/index.js index f1facb8764..b50f8b51c1 100644 --- a/src/rules/selector-attribute-operator-blacklist/__tests__/index.js +++ b/lib/rules/selector-attribute-operator-blacklist/__tests__/index.js @@ -1,19 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "*=", - "~=", - ]], + config: [[ "*=", "~=" ]], accept: [ { code: "a[target] { }", diff --git a/lib/rules/selector-attribute-operator-blacklist/index.js b/lib/rules/selector-attribute-operator-blacklist/index.js new file mode 100644 index 0000000000..438f264efd --- /dev/null +++ b/lib/rules/selector-attribute-operator-blacklist/index.js @@ -0,0 +1,60 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") + +const ruleName = "selector-attribute-operator-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: operator => `Unexpected operator "${operator}"`, +}) + +const rule = function (blacklistInput) { + const blacklist = [].concat(blacklistInput) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + if (rule.selector.indexOf("[") === -1 || rule.selector.indexOf("=") === -1) { + return + } + + parseSelector(rule.selector, result, rule, selectorTree => { + selectorTree.walkAttributes(attributeNode => { + const operator = attributeNode.operator + + if (!operator || operator && blacklist.indexOf(operator) === -1) { + return + } + + report({ + message: messages.rejected(operator), + node: rule, + index: attributeNode.attribute.length + 1, + result, + ruleName, + }) + }) + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-attribute-operator-space-after/README.md b/lib/rules/selector-attribute-operator-space-after/README.md similarity index 100% rename from src/rules/selector-attribute-operator-space-after/README.md rename to lib/rules/selector-attribute-operator-space-after/README.md diff --git a/src/rules/selector-attribute-operator-space-after/__tests__/index.js b/lib/rules/selector-attribute-operator-space-after/__tests__/index.js similarity index 99% rename from src/rules/selector-attribute-operator-space-after/__tests__/index.js rename to lib/rules/selector-attribute-operator-space-after/__tests__/index.js index 5edc536940..3423eed905 100644 --- a/src/rules/selector-attribute-operator-space-after/__tests__/index.js +++ b/lib/rules/selector-attribute-operator-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-attribute-operator-space-after/index.js b/lib/rules/selector-attribute-operator-space-after/index.js new file mode 100644 index 0000000000..1e8d0fd380 --- /dev/null +++ b/lib/rules/selector-attribute-operator-space-after/index.js @@ -0,0 +1,38 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const selectorAttributeOperatorSpaceChecker = require("../selectorAttributeOperatorSpaceChecker") + +const ruleName = "selector-attribute-operator-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: operator => `Expected single space after "${operator}"`, + rejectedAfter: operator => `Unexpected whitespace after "${operator}"`, +}) + +const rule = function (expectation) { + return (root, result) => { + const checker = whitespaceChecker("space", expectation, messages) + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + selectorAttributeOperatorSpaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + checkBeforeOperator: false, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-attribute-operator-space-before/README.md b/lib/rules/selector-attribute-operator-space-before/README.md similarity index 100% rename from src/rules/selector-attribute-operator-space-before/README.md rename to lib/rules/selector-attribute-operator-space-before/README.md diff --git a/src/rules/selector-attribute-operator-space-before/__tests__/index.js b/lib/rules/selector-attribute-operator-space-before/__tests__/index.js similarity index 99% rename from src/rules/selector-attribute-operator-space-before/__tests__/index.js rename to lib/rules/selector-attribute-operator-space-before/__tests__/index.js index e33c73070b..f1404802c6 100644 --- a/src/rules/selector-attribute-operator-space-before/__tests__/index.js +++ b/lib/rules/selector-attribute-operator-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-attribute-operator-space-before/index.js b/lib/rules/selector-attribute-operator-space-before/index.js new file mode 100644 index 0000000000..5b7755d6d6 --- /dev/null +++ b/lib/rules/selector-attribute-operator-space-before/index.js @@ -0,0 +1,38 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const selectorAttributeOperatorSpaceChecker = require("../selectorAttributeOperatorSpaceChecker") + +const ruleName = "selector-attribute-operator-space-before" + +const messages = ruleMessages(ruleName, { + expectedBefore: operator => `Expected single space before "${operator}"`, + rejectedBefore: operator => `Unexpected whitespace before "${operator}"`, +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + selectorAttributeOperatorSpaceChecker({ + root, + result, + locationChecker: checker.before, + checkedRuleName: ruleName, + checkBeforeOperator: true, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-attribute-operator-whitelist/README.md b/lib/rules/selector-attribute-operator-whitelist/README.md similarity index 100% rename from src/rules/selector-attribute-operator-whitelist/README.md rename to lib/rules/selector-attribute-operator-whitelist/README.md diff --git a/src/rules/selector-attribute-operator-whitelist/__tests__/index.js b/lib/rules/selector-attribute-operator-whitelist/__tests__/index.js similarity index 83% rename from src/rules/selector-attribute-operator-whitelist/__tests__/index.js rename to lib/rules/selector-attribute-operator-whitelist/__tests__/index.js index 6cb1717c6f..0cdc34a094 100644 --- a/src/rules/selector-attribute-operator-whitelist/__tests__/index.js +++ b/lib/rules/selector-attribute-operator-whitelist/__tests__/index.js @@ -1,19 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "=", - "|=", - ]], + config: [[ "=", "|=" ]], accept: [ { code: "a[target] { }", diff --git a/lib/rules/selector-attribute-operator-whitelist/index.js b/lib/rules/selector-attribute-operator-whitelist/index.js new file mode 100644 index 0000000000..be086ee0bf --- /dev/null +++ b/lib/rules/selector-attribute-operator-whitelist/index.js @@ -0,0 +1,60 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") + +const ruleName = "selector-attribute-operator-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: operator => `Unexpected operator "${operator}"`, +}) + +const rule = function (whitelistInput) { + const whitelist = [].concat(whitelistInput) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + if (rule.selector.indexOf("[") === -1 || rule.selector.indexOf("=") === -1) { + return + } + + parseSelector(rule.selector, result, rule, selectorTree => { + selectorTree.walkAttributes(attributeNode => { + const operator = attributeNode.operator + + if (!operator || operator && whitelist.indexOf(operator) !== -1) { + return + } + + report({ + message: messages.rejected(operator), + node: rule, + index: attributeNode.attribute.length + 1, + result, + ruleName, + }) + }) + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-attribute-quotes/README.md b/lib/rules/selector-attribute-quotes/README.md similarity index 100% rename from src/rules/selector-attribute-quotes/README.md rename to lib/rules/selector-attribute-quotes/README.md diff --git a/src/rules/selector-attribute-quotes/__tests__/index.js b/lib/rules/selector-attribute-quotes/__tests__/index.js similarity index 95% rename from src/rules/selector-attribute-quotes/__tests__/index.js rename to lib/rules/selector-attribute-quotes/__tests__/index.js index 1f5b1f3b04..72f84092a7 100644 --- a/src/rules/selector-attribute-quotes/__tests__/index.js +++ b/lib/rules/selector-attribute-quotes/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-attribute-quotes/index.js b/lib/rules/selector-attribute-quotes/index.js new file mode 100644 index 0000000000..57ec21422d --- /dev/null +++ b/lib/rules/selector-attribute-quotes/index.js @@ -0,0 +1,67 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-attribute-quotes" + +const messages = ruleMessages(ruleName, { + expected: value => `Expected quotes around "${value}"`, + rejected: value => `Unexpected quotes around "${value}"`, +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + if (rule.selector.indexOf("[") === -1 || rule.selector.indexOf("=") === -1) { + return + } + + parseSelector(rule.selector, result, rule, selectorTree => { + selectorTree.walkAttributes(attributeNode => { + if (!attributeNode.operator) { + return + } + + const attributeSelectorString = attributeNode.toString() + + if (!attributeNode.quoted && expectation === "always") { + complain(messages.expected(attributeNode.raws.unquoted), attributeNode.sourceIndex + attributeSelectorString.indexOf(attributeNode.value)) + } + + if (attributeNode.quoted && expectation === "never") { + complain(messages.rejected(attributeNode.raws.unquoted), attributeNode.sourceIndex + attributeSelectorString.indexOf(attributeNode.value)) + } + }) + }) + + function complain(message, index) { + report({ + message, + index, + result, + ruleName, + node: rule, + }) + } + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-class-pattern/README.md b/lib/rules/selector-class-pattern/README.md similarity index 100% rename from src/rules/selector-class-pattern/README.md rename to lib/rules/selector-class-pattern/README.md diff --git a/src/rules/selector-class-pattern/__tests__/index.js b/lib/rules/selector-class-pattern/__tests__/index.js similarity index 92% rename from src/rules/selector-class-pattern/__tests__/index.js rename to lib/rules/selector-class-pattern/__tests__/index.js index bd36a26822..6f290110bd 100644 --- a/src/rules/selector-class-pattern/__tests__/index.js +++ b/lib/rules/selector-class-pattern/__tests__/index.js @@ -1,12 +1,10 @@ -import { - mergeTestDescriptions, - testRule, -} from "../../../testUtils" -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" +"use strict" + +const mergeTestDescriptions = require("../../../testUtils/mergeTestDescriptions") +const testRule = require("../../../testUtils/testRule") +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") const rule = rules[ruleName] @@ -123,7 +121,7 @@ testRule(rule, { syntax: "scss", config: [ /^[A-Z]+$/, { resolveNestedSelectors: true } ], - accept:[ { + accept: [ { code: "@for $n from 1 through 5 { .A#{$n} { } }", description: "ignore sass interpolation inside @for", }, { @@ -140,7 +138,7 @@ testRule(rule, { syntax: "less", config: [/^[A-Z]+$/], - accept:[ { + accept: [ { code: ".mixin-name() { }", description: "ignore non-ouputting Less class mixin definition", }, { diff --git a/src/rules/selector-class-pattern/index.js b/lib/rules/selector-class-pattern/index.js similarity index 52% rename from src/rules/selector-class-pattern/index.js rename to lib/rules/selector-class-pattern/index.js index 2ae3a1f55d..5ac4ba9072 100644 --- a/src/rules/selector-class-pattern/index.js +++ b/lib/rules/selector-class-pattern/index.js @@ -1,22 +1,22 @@ -import { - isKeyframeSelector, - isStandardSyntaxRule, - isStandardSyntaxSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" -import resolveNestedSelector from "postcss-resolve-nested-selector" +"use strict" -export const ruleName = "selector-class-pattern" +const isKeyframeSelector = require("../../utils/isKeyframeSelector") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const resolveNestedSelector = require("postcss-resolve-nested-selector") -export const messages = ruleMessages(ruleName, { +const ruleName = "selector-class-pattern" + +const messages = ruleMessages(ruleName, { expected: selectorValue => `Expected class selector ".${selectorValue}" to match specified pattern`, }) -export default function (pattern, options) { +const rule = function (pattern, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: pattern, @@ -28,19 +28,26 @@ export default function (pattern, options) { }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } const shouldResolveNestedSelectors = _.get(options, "resolveNestedSelectors") - const normalizedPattern = (_.isString(pattern)) - ? new RegExp(pattern) - : pattern + const normalizedPattern = _.isString(pattern) ? new RegExp(pattern) : pattern root.walkRules(rule => { - const { selector, selectors } = rule + const selector = rule.selector, + selectors = rule.selectors - if (!isStandardSyntaxRule(rule)) { return } - if (!isStandardSyntaxSelector(selector)) { return } - if (selectors.some(s => isKeyframeSelector(s))) { return } + if (!isStandardSyntaxRule(rule)) { + return + } + if (!isStandardSyntaxSelector(selector)) { + return + } + if (selectors.some(s => isKeyframeSelector(s))) { + return + } // Only bother resolving selectors that have an interpolating & if (shouldResolveNestedSelectors && hasInterpolatingAmpersand(selector)) { @@ -54,8 +61,12 @@ export default function (pattern, options) { function checkSelector(fullSelector, rule) { fullSelector.walkClasses(classNode => { - const { value, sourceIndex } = classNode - if (normalizedPattern.test(value)) { return } + const value = classNode.value, + sourceIndex = classNode.sourceIndex + + if (normalizedPattern.test(value)) { + return + } report({ result, ruleName, @@ -73,13 +84,24 @@ export default function (pattern, options) { // stands on its own as a simple selector function hasInterpolatingAmpersand(selector) { for (let i = 0, l = selector.length; i < l; i++) { - if (selector[i] !== "&") { continue } - if (!_.isUndefined(selector[i - 1]) && !isCombinator(selector[i - 1])) { return true } - if (!_.isUndefined(selector[i + 1]) && !isCombinator(selector[i + 1])) { return true } + if (selector[i] !== "&") { + continue + } + if (!_.isUndefined(selector[i - 1]) && !isCombinator(selector[i - 1])) { + return true + } + if (!_.isUndefined(selector[i + 1]) && !isCombinator(selector[i + 1])) { + return true + } } return false } function isCombinator(x) { - return /[\s+>~]/.test(x) + return (/[\s+>~]/.test(x) + ) } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-combinator-space-after/README.md b/lib/rules/selector-combinator-space-after/README.md similarity index 100% rename from src/rules/selector-combinator-space-after/README.md rename to lib/rules/selector-combinator-space-after/README.md diff --git a/src/rules/selector-combinator-space-after/__tests__/index.js b/lib/rules/selector-combinator-space-after/__tests__/index.js similarity index 96% rename from src/rules/selector-combinator-space-after/__tests__/index.js rename to lib/rules/selector-combinator-space-after/__tests__/index.js index f207869d09..5035b54c2e 100644 --- a/src/rules/selector-combinator-space-after/__tests__/index.js +++ b/lib/rules/selector-combinator-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -223,7 +223,7 @@ testRule(rule, { syntax: "less", config: ["always"], - accept:[{ + accept: [{ code: ".a when (@size>=60) and (@size<102) {}", description: "ignore constructs", }], diff --git a/lib/rules/selector-combinator-space-after/index.js b/lib/rules/selector-combinator-space-after/index.js new file mode 100644 index 0000000000..c738269d8c --- /dev/null +++ b/lib/rules/selector-combinator-space-after/index.js @@ -0,0 +1,37 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const selectorCombinatorSpaceChecker = require("../selectorCombinatorSpaceChecker") + +const ruleName = "selector-combinator-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: combinator => `Expected single space after "${combinator}"`, + rejectedAfter: combinator => `Unexpected whitespace after "${combinator}"`, +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + selectorCombinatorSpaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-combinator-space-before/README.md b/lib/rules/selector-combinator-space-before/README.md similarity index 100% rename from src/rules/selector-combinator-space-before/README.md rename to lib/rules/selector-combinator-space-before/README.md diff --git a/src/rules/selector-combinator-space-before/__tests__/index.js b/lib/rules/selector-combinator-space-before/__tests__/index.js similarity index 96% rename from src/rules/selector-combinator-space-before/__tests__/index.js rename to lib/rules/selector-combinator-space-before/__tests__/index.js index dec0da6a26..df1e0035a1 100644 --- a/src/rules/selector-combinator-space-before/__tests__/index.js +++ b/lib/rules/selector-combinator-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -228,7 +228,7 @@ testRule(rule, { syntax: "less", config: ["always"], - accept:[{ + accept: [{ code: ".a when (@size>=60) and (@size<102) {}", description: "ignore constructs", }], diff --git a/lib/rules/selector-combinator-space-before/index.js b/lib/rules/selector-combinator-space-before/index.js new file mode 100644 index 0000000000..2646f6e32b --- /dev/null +++ b/lib/rules/selector-combinator-space-before/index.js @@ -0,0 +1,37 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const selectorCombinatorSpaceChecker = require("../selectorCombinatorSpaceChecker") + +const ruleName = "selector-combinator-space-before" + +const messages = ruleMessages(ruleName, { + expectedBefore: combinator => `Expected single space before "${combinator}"`, + rejectedBefore: combinator => `Unexpected whitespace before "${combinator}"`, +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never" ], + }) + if (!validOptions) { + return + } + + selectorCombinatorSpaceChecker({ + root, + result, + locationChecker: checker.before, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-descendant-combinator-no-non-space/README.md b/lib/rules/selector-descendant-combinator-no-non-space/README.md similarity index 100% rename from src/rules/selector-descendant-combinator-no-non-space/README.md rename to lib/rules/selector-descendant-combinator-no-non-space/README.md diff --git a/src/rules/selector-descendant-combinator-no-non-space/__tests__/index.js b/lib/rules/selector-descendant-combinator-no-non-space/__tests__/index.js similarity index 80% rename from src/rules/selector-descendant-combinator-no-non-space/__tests__/index.js rename to lib/rules/selector-descendant-combinator-no-non-space/__tests__/index.js index a9d1c2d7c8..ef6f362404 100644 --- a/src/rules/selector-descendant-combinator-no-non-space/__tests__/index.js +++ b/lib/rules/selector-descendant-combinator-no-non-space/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-descendant-combinator-no-non-space/index.js b/lib/rules/selector-descendant-combinator-no-non-space/index.js new file mode 100644 index 0000000000..f02f85b370 --- /dev/null +++ b/lib/rules/selector-descendant-combinator-no-non-space/index.js @@ -0,0 +1,56 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const punctuationSets = require("../../reference/punctuationSets") + +const ruleName = "selector-descendant-combinator-no-non-space" + +const messages = ruleMessages(ruleName, { + rejected: nonSpaceCharacter => `Unexpected "${nonSpaceCharacter}"`, +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + + const selector = rule.selector + + parseSelector(selector, result, rule, fullSelector => { + fullSelector.walkCombinators(combinatorNode => { + const value = combinatorNode.value + + if (punctuationSets.nonSpaceCombinators.has(value)) { + return + } + if (value === " ") { + return + } + + report({ + result, + ruleName, + message: messages.rejected(value), + node: rule, + index: combinatorNode.sourceIndex, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-id-pattern/README.md b/lib/rules/selector-id-pattern/README.md similarity index 100% rename from src/rules/selector-id-pattern/README.md rename to lib/rules/selector-id-pattern/README.md diff --git a/src/rules/selector-id-pattern/__tests__/index.js b/lib/rules/selector-id-pattern/__tests__/index.js similarity index 77% rename from src/rules/selector-id-pattern/__tests__/index.js rename to lib/rules/selector-id-pattern/__tests__/index.js index f1aa7f9bc6..99cc293cdf 100644 --- a/src/rules/selector-id-pattern/__tests__/index.js +++ b/lib/rules/selector-id-pattern/__tests__/index.js @@ -1,12 +1,10 @@ -import { - mergeTestDescriptions, - testRule, -} from "../../../testUtils" -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" +"use strict" + +const mergeTestDescriptions = require("../../../testUtils/mergeTestDescriptions") +const testRule = require("../../../testUtils/testRule") +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") const rule = rules[ruleName] @@ -55,7 +53,7 @@ testRule(rule, { syntax: "scss", config: [/^[A-Z]+$/], - accept:[ { + accept: [ { code: "@for $n from 1 through 5 { #a#{$n} { } }", description: "ignore sass interpolation inside @for", }, { diff --git a/lib/rules/selector-id-pattern/index.js b/lib/rules/selector-id-pattern/index.js new file mode 100644 index 0000000000..5e422afac9 --- /dev/null +++ b/lib/rules/selector-id-pattern/index.js @@ -0,0 +1,67 @@ +"use strict" + +const _ = require("lodash") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-id-pattern" + +const messages = ruleMessages(ruleName, { + expected: selectorValue => `Expected id selector "#${selectorValue}" to match specified pattern`, +}) + +const rule = function (pattern) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: pattern, + possible: [ _.isRegExp, _.isString ], + }) + if (!validOptions) { + return + } + + const normalizedPattern = _.isString(pattern) ? new RegExp(pattern) : pattern + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + + const selector = rule.selector + + if (!isStandardSyntaxSelector(selector)) { + return + } + + parseSelector(selector, result, rule, fullSelector => { + fullSelector.walk(selectorNode => { + if (selectorNode.type !== "id") { + return + } + const value = selectorNode.value, + sourceIndex = selectorNode.sourceIndex + + if (normalizedPattern.test(value)) { + return + } + + report({ + result, + ruleName, + message: messages.expected(value), + node: rule, + index: sourceIndex, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-list-comma-newline-after/README.md b/lib/rules/selector-list-comma-newline-after/README.md similarity index 100% rename from src/rules/selector-list-comma-newline-after/README.md rename to lib/rules/selector-list-comma-newline-after/README.md diff --git a/src/rules/selector-list-comma-newline-after/__tests__/index.js b/lib/rules/selector-list-comma-newline-after/__tests__/index.js similarity index 96% rename from src/rules/selector-list-comma-newline-after/__tests__/index.js rename to lib/rules/selector-list-comma-newline-after/__tests__/index.js index 5644d23a82..5eab2ec25b 100644 --- a/src/rules/selector-list-comma-newline-after/__tests__/index.js +++ b/lib/rules/selector-list-comma-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-list-comma-newline-after/index.js b/lib/rules/selector-list-comma-newline-after/index.js new file mode 100644 index 0000000000..07fd745a7c --- /dev/null +++ b/lib/rules/selector-list-comma-newline-after/index.js @@ -0,0 +1,71 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const styleSearch = require("style-search") + +const ruleName = "selector-list-comma-newline-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected newline after \",\"", + expectedAfterMultiLine: () => "Expected newline after \",\" in a multi-line list", + rejectedAfterMultiLine: () => "Unexpected whitespace after \",\" in a multi-line list", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("newline", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "always-multi-line", "never-multi-line" ], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + // Get raw selector so we can allow end-of-line comments, e.g. + // a, /* comment */ + // b {} + const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector + styleSearch({ + source: selector, + target: ",", + functionArguments: "skip", + }, match => { + const nextThreeChars = selector.substr(match.endIndex, 3) + + // If there's a // comment, that means there has to be a newline + // ending the comment so we're fine + if (nextThreeChars === " //") { + return + } + + // If there is a space and then a comment begins, look for the newline + // after that comment + const indextoCheckAfter = nextThreeChars === " /*" ? selector.indexOf("*/", match.endIndex) + 1 : match.startIndex + checker.afterOneOnly({ + source: selector, + index: indextoCheckAfter, + err: m => report({ + message: m, + node: rule, + index: match.startIndex, + result, + ruleName, + }), + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-list-comma-newline-before/README.md b/lib/rules/selector-list-comma-newline-before/README.md similarity index 100% rename from src/rules/selector-list-comma-newline-before/README.md rename to lib/rules/selector-list-comma-newline-before/README.md diff --git a/src/rules/selector-list-comma-newline-before/__tests__/index.js b/lib/rules/selector-list-comma-newline-before/__tests__/index.js similarity index 94% rename from src/rules/selector-list-comma-newline-before/__tests__/index.js rename to lib/rules/selector-list-comma-newline-before/__tests__/index.js index 7bd91c3e3c..5686bd3300 100644 --- a/src/rules/selector-list-comma-newline-before/__tests__/index.js +++ b/lib/rules/selector-list-comma-newline-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-list-comma-newline-before/index.js b/lib/rules/selector-list-comma-newline-before/index.js new file mode 100644 index 0000000000..df69a50b2c --- /dev/null +++ b/lib/rules/selector-list-comma-newline-before/index.js @@ -0,0 +1,38 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const selectorListCommaWhitespaceChecker = require("../selectorListCommaWhitespaceChecker") + +const ruleName = "selector-list-comma-newline-before" + +const messages = ruleMessages(ruleName, { + expectedBefore: () => "Expected newline before \",\"", + expectedBeforeMultiLine: () => "Expected newline before \",\" in a multi-line list", + rejectedBeforeMultiLine: () => "Unexpected whitespace before \",\" in a multi-line list", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("newline", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "always-multi-line", "never-multi-line" ], + }) + if (!validOptions) { + return + } + + selectorListCommaWhitespaceChecker({ + root, + result, + locationChecker: checker.beforeAllowingIndentation, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-list-comma-space-after/README.md b/lib/rules/selector-list-comma-space-after/README.md similarity index 100% rename from src/rules/selector-list-comma-space-after/README.md rename to lib/rules/selector-list-comma-space-after/README.md diff --git a/src/rules/selector-list-comma-space-after/__tests__/index.js b/lib/rules/selector-list-comma-space-after/__tests__/index.js similarity index 95% rename from src/rules/selector-list-comma-space-after/__tests__/index.js rename to lib/rules/selector-list-comma-space-after/__tests__/index.js index f8c3b2b990..d365a2a2a1 100644 --- a/src/rules/selector-list-comma-space-after/__tests__/index.js +++ b/lib/rules/selector-list-comma-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-list-comma-space-after/index.js b/lib/rules/selector-list-comma-space-after/index.js new file mode 100644 index 0000000000..b9581c7f2d --- /dev/null +++ b/lib/rules/selector-list-comma-space-after/index.js @@ -0,0 +1,39 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const selectorListCommaWhitespaceChecker = require("../selectorListCommaWhitespaceChecker") + +const ruleName = "selector-list-comma-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected single space after \",\"", + rejectedAfter: () => "Unexpected whitespace after \",\"", + expectedAfterSingleLine: () => "Expected single space after \",\" in a single-line list", + rejectedAfterSingleLine: () => "Unexpected whitespace after \",\" in a single-line list", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never", "always-single-line", "never-single-line" ], + }) + if (!validOptions) { + return + } + + selectorListCommaWhitespaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-list-comma-space-before/README.md b/lib/rules/selector-list-comma-space-before/README.md similarity index 100% rename from src/rules/selector-list-comma-space-before/README.md rename to lib/rules/selector-list-comma-space-before/README.md diff --git a/src/rules/selector-list-comma-space-before/__tests__/index.js b/lib/rules/selector-list-comma-space-before/__tests__/index.js similarity index 95% rename from src/rules/selector-list-comma-space-before/__tests__/index.js rename to lib/rules/selector-list-comma-space-before/__tests__/index.js index 25fa4e4c8d..85a6b71c80 100644 --- a/src/rules/selector-list-comma-space-before/__tests__/index.js +++ b/lib/rules/selector-list-comma-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/selector-list-comma-space-before/index.js b/lib/rules/selector-list-comma-space-before/index.js similarity index 51% rename from src/rules/selector-list-comma-space-before/index.js rename to lib/rules/selector-list-comma-space-before/index.js index e05f738e49..4e574bc628 100644 --- a/src/rules/selector-list-comma-space-before/index.js +++ b/lib/rules/selector-list-comma-space-before/index.js @@ -1,33 +1,29 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" +"use strict" -import { selectorListCommaWhitespaceChecker } from "../selector-list-comma-space-after" +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const selectorListCommaWhitespaceChecker = require("../selectorListCommaWhitespaceChecker") -export const ruleName = "selector-list-comma-space-before" +const ruleName = "selector-list-comma-space-before" -export const messages = ruleMessages(ruleName, { +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected single space before \",\"", rejectedBefore: () => "Unexpected whitespace before \",\"", expectedBeforeSingleLine: () => "Expected single space before \",\" in a single-line list", rejectedBeforeSingleLine: () => "Unexpected whitespace before \",\" in a single-line list", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } selectorListCommaWhitespaceChecker({ root, @@ -37,3 +33,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-max-compound-selectors/README.md b/lib/rules/selector-max-compound-selectors/README.md similarity index 100% rename from src/rules/selector-max-compound-selectors/README.md rename to lib/rules/selector-max-compound-selectors/README.md diff --git a/src/rules/selector-max-compound-selectors/__tests__/index.js b/lib/rules/selector-max-compound-selectors/__tests__/index.js similarity index 96% rename from src/rules/selector-max-compound-selectors/__tests__/index.js rename to lib/rules/selector-max-compound-selectors/__tests__/index.js index 98e8b89a38..257ba4bcd6 100644 --- a/src/rules/selector-max-compound-selectors/__tests__/index.js +++ b/lib/rules/selector-max-compound-selectors/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/selector-max-compound-selectors/index.js b/lib/rules/selector-max-compound-selectors/index.js similarity index 63% rename from src/rules/selector-max-compound-selectors/index.js rename to lib/rules/selector-max-compound-selectors/index.js index 24914032fb..ebb3b6c13a 100644 --- a/src/rules/selector-max-compound-selectors/index.js +++ b/lib/rules/selector-max-compound-selectors/index.js @@ -1,20 +1,20 @@ -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import resolvedNestedSelector from "postcss-resolve-nested-selector" -import selectorParser from "postcss-selector-parser" +"use strict" -export const ruleName = "selector-max-compound-selectors" +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const resolvedNestedSelector = require("postcss-resolve-nested-selector") +const selectorParser = require("postcss-selector-parser") -export const messages = ruleMessages(ruleName, { +const ruleName = "selector-max-compound-selectors" + +const messages = ruleMessages(ruleName, { expected: (selector, max) => `Expected "${selector}" to have no more than ${max} compound selectors`, }) -export default function (max) { +const rule = function (max) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: max, @@ -22,7 +22,9 @@ export default function (max) { return typeof max === "number" && max > 0 }], }) - if (!validOptions) { return } + if (!validOptions) { + return + } // Finds actual selectors in selectorNode object and checks them function checkSelector(selectorNode, rule) { @@ -35,7 +37,9 @@ export default function (max) { } // Compound selectors are separated by combinators, so increase count when meeting one - if (childNode.type === "combinator") { compoundCount++ } + if (childNode.type === "combinator") { + compoundCount++ + } }) if (selectorNode.type !== "root" && selectorNode.type !== "pseudo" && compoundCount > max) { @@ -50,15 +54,21 @@ export default function (max) { } root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (!isStandardSyntaxSelector(rule.selector)) { return } + if (!isStandardSyntaxRule(rule)) { + return + } + if (!isStandardSyntaxSelector(rule.selector)) { + return + } // Nested selectors are processed in steps, as nesting levels are resolved. // Here we skip processing the intermediate parts of selectors (to process only fully resolved selectors) - if (rule.nodes.some(node => node.type === "rule" || node.type === "atrule")) { return } + if (rule.nodes.some(node => node.type === "rule" || node.type === "atrule")) { + return + } // Using `rule.selectors` gets us each selector if there is a comma separated set - rule.selectors.forEach((selector) => { + rule.selectors.forEach(selector => { resolvedNestedSelector(selector, rule).forEach(resolvedSelector => { // Process each resolved selector with `checkSelector` via postcss-selector-parser selectorParser(s => checkSelector(s, rule)).process(resolvedSelector) @@ -67,3 +77,7 @@ export default function (max) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-max-empty-lines/README.md b/lib/rules/selector-max-empty-lines/README.md similarity index 100% rename from src/rules/selector-max-empty-lines/README.md rename to lib/rules/selector-max-empty-lines/README.md diff --git a/src/rules/selector-max-empty-lines/__tests__/index.js b/lib/rules/selector-max-empty-lines/__tests__/index.js similarity index 99% rename from src/rules/selector-max-empty-lines/__tests__/index.js rename to lib/rules/selector-max-empty-lines/__tests__/index.js index 7716712a1c..4546aba3be 100644 --- a/src/rules/selector-max-empty-lines/__tests__/index.js +++ b/lib/rules/selector-max-empty-lines/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-max-empty-lines/index.js b/lib/rules/selector-max-empty-lines/index.js new file mode 100644 index 0000000000..3f9a93cb4b --- /dev/null +++ b/lib/rules/selector-max-empty-lines/index.js @@ -0,0 +1,55 @@ +"use strict" + +const _ = require("lodash") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") + +const ruleName = "selector-max-empty-lines" + +const messages = ruleMessages(ruleName, { + expected: max => `Expected no more than ${max} empty line(s)`, +}) + +const rule = function (max) { + const maxAdjacentNewlines = max + 1 + + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: max, + possible: _.isNumber, + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + const selector = rule.raws.selector ? rule.raws.selector.raw : rule.selector + const repeatLFNewLines = _.repeat("\n", maxAdjacentNewlines) + const repeatCRLFNewLines = _.repeat("\r\n", maxAdjacentNewlines) + + styleSearch({ source: selector, target: "\n" }, match => { + if (selector.substr(match.startIndex + 1, maxAdjacentNewlines) === repeatLFNewLines || selector.substr(match.startIndex + 1, maxAdjacentNewlines * 2) === repeatCRLFNewLines) { + // Put index at `\r` if it's CRLF, otherwise leave it at `\n` + let index = match.startIndex + if (selector[index - 1] === "\r") { + index -= 1 + } + + report({ + message: messages.expected(max), + node: rule, + index, + result, + ruleName, + }) + } + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-max-specificity/README.md b/lib/rules/selector-max-specificity/README.md similarity index 100% rename from src/rules/selector-max-specificity/README.md rename to lib/rules/selector-max-specificity/README.md diff --git a/src/rules/selector-max-specificity/__tests__/index.js b/lib/rules/selector-max-specificity/__tests__/index.js similarity index 95% rename from src/rules/selector-max-specificity/__tests__/index.js rename to lib/rules/selector-max-specificity/__tests__/index.js index cfce92fb96..87abe6411b 100755 --- a/src/rules/selector-max-specificity/__tests__/index.js +++ b/lib/rules/selector-max-specificity/__tests__/index.js @@ -1,11 +1,11 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import stylelint from "../../.." -import test from "tape" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const stylelint = require("../../..") +const test = require("tape") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/selector-max-specificity/index.js b/lib/rules/selector-max-specificity/index.js similarity index 52% rename from src/rules/selector-max-specificity/index.js rename to lib/rules/selector-max-specificity/index.js index 390af48fe2..c005883497 100755 --- a/src/rules/selector-max-specificity/index.js +++ b/lib/rules/selector-max-specificity/index.js @@ -1,20 +1,20 @@ -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { compare } from "specificity" -import resolvedNestedSelector from "postcss-resolve-nested-selector" +"use strict" -export const ruleName = "selector-max-specificity" +const specificity = require("specificity") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const resolvedNestedSelector = require("postcss-resolve-nested-selector") -export const messages = ruleMessages(ruleName, { +const ruleName = "selector-max-specificity" + +const messages = ruleMessages(ruleName, { expected: (selector, specificity) => `Expected "${selector}" to have a specificity no more than "${specificity}"`, }) -export default function (max) { +const rule = function (max) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: max, @@ -24,22 +24,32 @@ export default function (max) { return pattern.test(max) }], }) - if (!validOptions) { return } + if (!validOptions) { + return + } const maxSpecificityArray = ("0," + max).split(",").map(parseFloat) root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (!isStandardSyntaxSelector(rule.selector)) { return } + if (!isStandardSyntaxRule(rule)) { + return + } + if (!isStandardSyntaxSelector(rule.selector)) { + return + } // Using rule.selectors gets us each selector in the eventuality we have a comma separated set rule.selectors.forEach(selector => { resolvedNestedSelector(selector, rule).forEach(resolvedSelector => { // Return early if selector contains a not pseudo-class - if (selector.indexOf(":not(") !== -1) { return } + if (selector.indexOf(":not(") !== -1) { + return + } // Return early if selector contains a matches - if (selector.indexOf(":matches(") !== -1) { return } + if (selector.indexOf(":matches(") !== -1) { + return + } // Check if the selector specificity exceeds the allowed maximum try { - if (compare(resolvedSelector, maxSpecificityArray) === 1) { + if (specificity.compare(resolvedSelector, maxSpecificityArray) === 1) { report({ ruleName, result, @@ -49,10 +59,14 @@ export default function (max) { }) } } catch (e) { - result.warn("Cannot parse selector", { node : rule }) + result.warn("Cannot parse selector", { node: rule }) } }) }) }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-nested-pattern/README.md b/lib/rules/selector-nested-pattern/README.md similarity index 100% rename from src/rules/selector-nested-pattern/README.md rename to lib/rules/selector-nested-pattern/README.md diff --git a/src/rules/selector-nested-pattern/__tests__/index.js b/lib/rules/selector-nested-pattern/__tests__/index.js similarity index 82% rename from src/rules/selector-nested-pattern/__tests__/index.js rename to lib/rules/selector-nested-pattern/__tests__/index.js index f3f5659b29..c2e409d339 100644 --- a/src/rules/selector-nested-pattern/__tests__/index.js +++ b/lib/rules/selector-nested-pattern/__tests__/index.js @@ -1,13 +1,11 @@ -import { - mergeTestDescriptions, - testRule, -} from "../../../testUtils" -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { stripIndent } from "common-tags" +"use strict" + +const mergeTestDescriptions = require("../../../testUtils/mergeTestDescriptions") +const testRule = require("../../../testUtils/testRule") +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const stripIndent = require("common-tags").stripIndent const rule = rules[ruleName] diff --git a/lib/rules/selector-nested-pattern/index.js b/lib/rules/selector-nested-pattern/index.js new file mode 100644 index 0000000000..9b90f6eb7a --- /dev/null +++ b/lib/rules/selector-nested-pattern/index.js @@ -0,0 +1,58 @@ +"use strict" + +const _ = require("lodash") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-nested-pattern" + +const messages = ruleMessages(ruleName, { + expected: selector => `Expected nested selector "${selector}" to match specified pattern`, +}) + +const rule = function (pattern) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: pattern, + possible: [ _.isRegExp, _.isString ], + }) + if (!validOptions) { + return + } + + const normalizedPattern = _.isString(pattern) ? new RegExp(pattern) : pattern + + root.walkRules(rule => { + if (rule.parent.type !== "rule") { + return + } + if (!isStandardSyntaxRule(rule)) { + return + } + + const selector = rule.selector + + if (!isStandardSyntaxSelector(selector)) { + return + } + + if (normalizedPattern.test(selector)) { + return + } + + report({ + result, + ruleName, + message: messages.expected(selector), + node: rule, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-no-attribute/README.md b/lib/rules/selector-no-attribute/README.md similarity index 100% rename from src/rules/selector-no-attribute/README.md rename to lib/rules/selector-no-attribute/README.md diff --git a/src/rules/selector-no-attribute/__tests__/index.js b/lib/rules/selector-no-attribute/__tests__/index.js similarity index 79% rename from src/rules/selector-no-attribute/__tests__/index.js rename to lib/rules/selector-no-attribute/__tests__/index.js index 88c57185cf..3a2828459a 100644 --- a/src/rules/selector-no-attribute/__tests__/index.js +++ b/lib/rules/selector-no-attribute/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-no-attribute/index.js b/lib/rules/selector-no-attribute/index.js new file mode 100644 index 0000000000..1f48272043 --- /dev/null +++ b/lib/rules/selector-no-attribute/index.js @@ -0,0 +1,49 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-no-attribute" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected attribute selector", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + + if (!isStandardSyntaxSelector(selector)) { + return + } + parseSelector(selector, result, rule, selectorAST => { + selectorAST.walkAttributes(attribute => { + report({ + message: messages.rejected, + node: rule, + index: attribute.sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-no-combinator/README.md b/lib/rules/selector-no-combinator/README.md similarity index 100% rename from src/rules/selector-no-combinator/README.md rename to lib/rules/selector-no-combinator/README.md diff --git a/src/rules/selector-no-combinator/__tests__/index.js b/lib/rules/selector-no-combinator/__tests__/index.js similarity index 94% rename from src/rules/selector-no-combinator/__tests__/index.js rename to lib/rules/selector-no-combinator/__tests__/index.js index 3f4bd53487..709f5e1fb9 100644 --- a/src/rules/selector-no-combinator/__tests__/index.js +++ b/lib/rules/selector-no-combinator/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-no-combinator/index.js b/lib/rules/selector-no-combinator/index.js new file mode 100644 index 0000000000..014b39fe85 --- /dev/null +++ b/lib/rules/selector-no-combinator/index.js @@ -0,0 +1,49 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-no-combinator" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected combinator", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + + if (!isStandardSyntaxSelector(selector)) { + return + } + parseSelector(selector, result, rule, selectorAST => { + selectorAST.walkCombinators(combinator => { + report({ + message: messages.rejected, + node: rule, + index: combinator.sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-no-empty/README.md b/lib/rules/selector-no-empty/README.md similarity index 100% rename from src/rules/selector-no-empty/README.md rename to lib/rules/selector-no-empty/README.md diff --git a/src/rules/selector-no-empty/__tests__/index.js b/lib/rules/selector-no-empty/__tests__/index.js similarity index 93% rename from src/rules/selector-no-empty/__tests__/index.js rename to lib/rules/selector-no-empty/__tests__/index.js index 48fa43f6d0..193f407d7d 100644 --- a/src/rules/selector-no-empty/__tests__/index.js +++ b/lib/rules/selector-no-empty/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-no-empty/index.js b/lib/rules/selector-no-empty/index.js new file mode 100644 index 0000000000..fbcbb153ea --- /dev/null +++ b/lib/rules/selector-no-empty/index.js @@ -0,0 +1,44 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-no-empty" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected empty selector", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + let index = 0 + + rule.selector.split(",").forEach(item => { + index += item.length + 1 + + if (item.trim() !== "") { + return + } + + report({ + message: messages.rejected, + node: rule, + index: index - 1, + ruleName, + result, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-no-id/README.md b/lib/rules/selector-no-id/README.md similarity index 100% rename from src/rules/selector-no-id/README.md rename to lib/rules/selector-no-id/README.md diff --git a/src/rules/selector-no-id/__tests__/index.js b/lib/rules/selector-no-id/__tests__/index.js similarity index 93% rename from src/rules/selector-no-id/__tests__/index.js rename to lib/rules/selector-no-id/__tests__/index.js index d5202ce399..a227ba4ce5 100644 --- a/src/rules/selector-no-id/__tests__/index.js +++ b/lib/rules/selector-no-id/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -100,6 +100,5 @@ testRule(rule, { }, { code: ".for(@n: 1) when (@n <= 10) { .n-@{n} #foo { } .for(@n + 1) }", description: "ignore Less interpolation + id inside .for", - }, - ], + } ], }) diff --git a/lib/rules/selector-no-id/index.js b/lib/rules/selector-no-id/index.js new file mode 100644 index 0000000000..e054fd54c0 --- /dev/null +++ b/lib/rules/selector-no-id/index.js @@ -0,0 +1,57 @@ +"use strict" + +const isKeyframeRule = require("../../utils/isKeyframeRule") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-no-id" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected id selector", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + if (isKeyframeRule(rule)) { + return + } + const selector = rule.selector + + if (!isStandardSyntaxSelector(selector)) { + return + } + parseSelector(selector, result, rule, selectorAST => { + selectorAST.walkIds(idNode => { + if (idNode.parent.parent.type === "pseudo") { + return + } + + report({ + message: messages.rejected, + node: rule, + index: idNode.sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-no-qualifying-type/README.md b/lib/rules/selector-no-qualifying-type/README.md similarity index 100% rename from src/rules/selector-no-qualifying-type/README.md rename to lib/rules/selector-no-qualifying-type/README.md diff --git a/src/rules/selector-no-qualifying-type/__tests__/index.js b/lib/rules/selector-no-qualifying-type/__tests__/index.js similarity index 99% rename from src/rules/selector-no-qualifying-type/__tests__/index.js rename to lib/rules/selector-no-qualifying-type/__tests__/index.js index e16f5e583a..9134407a57 100644 --- a/src/rules/selector-no-qualifying-type/__tests__/index.js +++ b/lib/rules/selector-no-qualifying-type/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/selector-no-qualifying-type/index.js b/lib/rules/selector-no-qualifying-type/index.js similarity index 55% rename from src/rules/selector-no-qualifying-type/index.js rename to lib/rules/selector-no-qualifying-type/index.js index 4aff66d6bc..fa510a59d0 100644 --- a/src/rules/selector-no-qualifying-type/index.js +++ b/lib/rules/selector-no-qualifying-type/index.js @@ -1,24 +1,22 @@ -import { - isKeyframeRule, - isStandardSyntaxRule, - isStandardSyntaxSelector, - optionsMatches, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import resolvedNestedSelector from "postcss-resolve-nested-selector" - -export const ruleName = "selector-no-qualifying-type" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const isKeyframeRule = require("../../utils/isKeyframeRule") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const optionsMatches = require("../../utils/optionsMatches") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const resolvedNestedSelector = require("postcss-resolve-nested-selector") + +const ruleName = "selector-no-qualifying-type" + +const messages = ruleMessages(ruleName, { rejected: "Unexpected qualifying type selector", }) -const selectorCharacters = [ - "#", ".", "[", -] +const selectorCharacters = [ "#", ".", "[" ] function isSelectorCharacters(value) { return selectorCharacters.some(char => value.indexOf(char) !== -1) @@ -29,11 +27,12 @@ function getRightNodes(node) { let rightNode = node while ((rightNode = rightNode.next())) { - if (rightNode.type === "combinator") { break } - if (rightNode.type !== "id" - && rightNode.type !== "class" - && rightNode.type !== "attribute" - ) { continue } + if (rightNode.type === "combinator") { + break + } + if (rightNode.type !== "id" && rightNode.type !== "class" && rightNode.type !== "attribute") { + continue + } result.push(rightNode) } @@ -41,7 +40,7 @@ function getRightNodes(node) { return result } -export default (enabled, options) => { +const rule = function (enabled, options) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: enabled, @@ -53,14 +52,24 @@ export default (enabled, options) => { }, optional: true, }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (isKeyframeRule(rule)) { return } + if (!isStandardSyntaxRule(rule)) { + return + } + if (isKeyframeRule(rule)) { + return + } // Increasing performance - if (!isStandardSyntaxSelector(rule.selector)) { return } - if (!isSelectorCharacters(rule.selector)) { return } + if (!isStandardSyntaxSelector(rule.selector)) { + return + } + if (!isSelectorCharacters(rule.selector)) { + return + } function checkSelector(selectorAST) { selectorAST.walkTags(selector => { @@ -73,7 +82,7 @@ export default (enabled, options) => { const selectorNodes = getRightNodes(selector) const index = selector.sourceIndex - selectorNodes.forEach((selectorNode) => { + selectorNodes.forEach(selectorNode => { if (selectorNode.type === "id" && !optionsMatches(options, "ignore", "id")) { complain(index) } @@ -90,7 +99,9 @@ export default (enabled, options) => { } resolvedNestedSelector(rule.selector, rule).forEach(resolvedSelector => { - if (!isStandardSyntaxSelector(resolvedSelector)) { return } + if (!isStandardSyntaxSelector(resolvedSelector)) { + return + } parseSelector(resolvedSelector, result, rule, checkSelector) }) @@ -107,3 +118,7 @@ export default (enabled, options) => { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-no-type/README.md b/lib/rules/selector-no-type/README.md similarity index 100% rename from src/rules/selector-no-type/README.md rename to lib/rules/selector-no-type/README.md diff --git a/src/rules/selector-no-type/__tests__/index.js b/lib/rules/selector-no-type/__tests__/index.js similarity index 94% rename from src/rules/selector-no-type/__tests__/index.js rename to lib/rules/selector-no-type/__tests__/index.js index 2ce61ffc26..5076bf6b76 100644 --- a/src/rules/selector-no-type/__tests__/index.js +++ b/lib/rules/selector-no-type/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -173,9 +173,7 @@ testRule(rule, { skipBasicChecks: true, syntax: "scss", - accept: [ - { code: "// Comment\n.c {}" }, - ], + accept: [{ code: "// Comment\n.c {}" }], reject: [{ code: "@for $n from 1 through 5 { .foo-#{$n} { div { content: \"#{$n}\"; } } }", @@ -203,9 +201,7 @@ testRule(rule, { skipBasicChecks: true, syntax: "less", - accept: [ - { code: "// Comment\n.c {}" }, - ], + accept: [{ code: "// Comment\n.c {}" }], reject: [{ code: ".for(@n: 1) when (@n <= 5) { .foo-@{n} { div { content: \"@{n}\"; } } .for (@n + 1); }", diff --git a/lib/rules/selector-no-type/index.js b/lib/rules/selector-no-type/index.js new file mode 100644 index 0000000000..f8a1e4f0e0 --- /dev/null +++ b/lib/rules/selector-no-type/index.js @@ -0,0 +1,117 @@ +"use strict" + +const _ = require("lodash") +const isKeyframeSelector = require("../../utils/isKeyframeSelector") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const isStandardSyntaxTypeSelector = require("../../utils/isStandardSyntaxTypeSelector") +const optionsMatches = require("../../utils/optionsMatches") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const resolveNestedSelector = require("postcss-resolve-nested-selector") + +const ruleName = "selector-no-type" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected type selector", +}) + +const rule = function (on, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual: on }, { + actual: options, + possible: { + ignore: [ "descendant", "compounded" ], + ignoreTypes: [_.isString], + }, + optional: true, + }) + if (!validOptions) { + return + } + + const ignoreDescendant = optionsMatches(options, "ignore", "descendant") + const ignoreCompounded = optionsMatches(options, "ignore", "compounded") + + root.walkRules(rule => { + const selector = rule.selector, + selectors = rule.selectors + + if (!isStandardSyntaxRule(rule)) { + return + } + if (!isStandardSyntaxSelector(selector)) { + return + } + if (selectors.some(s => isKeyframeSelector(s))) { + return + } + + if (ignoreDescendant) { + // Resolve each selector within the list before checking + selectors.forEach(selector => { + resolveNestedSelector(selector, rule).forEach(selector => { + checkSelector(selector, rule) + }) + }) + } else { + checkSelector(selector, rule) + } + }) + + function checkSelector(selector, rule) { + parseSelector(selector, result, rule, selectorAST => { + selectorAST.walkTags(tag => { + if (!isStandardSyntaxTypeSelector(tag)) { + return + } + + if (optionsMatches(options, "ignoreTypes", tag.value)) { + return + } + + if (ignoreDescendant && hasCombinatorBefore(tag)) { + return + } + + if (ignoreCompounded && isCompounded(tag)) { + return + } + + report({ + message: messages.rejected, + node: rule, + index: tag.sourceIndex, + ruleName, + result, + }) + }) + }) + } + } +} + +function hasCombinatorBefore(node) { + return node.parent.nodes.slice(0, node.parent.nodes.indexOf(node)).some(isCombinator) +} + +function isCompounded(node) { + if (node.prev() && !isCombinator(node.prev())) { + return true + } + if (node.next() && !isCombinator(node.next())) { + return true + } + return false +} + +function isCombinator(node) { + if (!node) return false + return _.get(node, "type") === "combinator" +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-no-universal/README.md b/lib/rules/selector-no-universal/README.md similarity index 100% rename from src/rules/selector-no-universal/README.md rename to lib/rules/selector-no-universal/README.md diff --git a/src/rules/selector-no-universal/__tests__/index.js b/lib/rules/selector-no-universal/__tests__/index.js similarity index 77% rename from src/rules/selector-no-universal/__tests__/index.js rename to lib/rules/selector-no-universal/__tests__/index.js index f9f32139a5..f88933b2e9 100644 --- a/src/rules/selector-no-universal/__tests__/index.js +++ b/lib/rules/selector-no-universal/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-no-universal/index.js b/lib/rules/selector-no-universal/index.js new file mode 100644 index 0000000000..3f9e5c047d --- /dev/null +++ b/lib/rules/selector-no-universal/index.js @@ -0,0 +1,49 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-no-universal" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected universal selector", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + + if (!isStandardSyntaxSelector(selector)) { + return + } + parseSelector(selector, result, rule, selectorAST => { + selectorAST.walkUniversals(universal => { + report({ + message: messages.rejected, + node: rule, + index: universal.sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-no-vendor-prefix/README.md b/lib/rules/selector-no-vendor-prefix/README.md similarity index 100% rename from src/rules/selector-no-vendor-prefix/README.md rename to lib/rules/selector-no-vendor-prefix/README.md diff --git a/src/rules/selector-no-vendor-prefix/__tests__/index.js b/lib/rules/selector-no-vendor-prefix/__tests__/index.js similarity index 88% rename from src/rules/selector-no-vendor-prefix/__tests__/index.js rename to lib/rules/selector-no-vendor-prefix/__tests__/index.js index 69b8c1549b..8c076017fe 100644 --- a/src/rules/selector-no-vendor-prefix/__tests__/index.js +++ b/lib/rules/selector-no-vendor-prefix/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-no-vendor-prefix/index.js b/lib/rules/selector-no-vendor-prefix/index.js new file mode 100644 index 0000000000..3df871493d --- /dev/null +++ b/lib/rules/selector-no-vendor-prefix/index.js @@ -0,0 +1,52 @@ +"use strict" + +const isAutoprefixable = require("../../utils/isAutoprefixable") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-no-vendor-prefix" + +const messages = ruleMessages(ruleName, { + rejected: selector => `Unexpected vendor-prefix "${selector}"`, +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + + if (!isStandardSyntaxSelector(selector)) { + return + } + parseSelector(selector, result, rule, selectorTree => { + selectorTree.walkPseudos(pseudoNode => { + if (isAutoprefixable.selector(pseudoNode.value)) { + report({ + result, + ruleName, + message: messages.rejected(pseudoNode.value), + node: rule, + index: rule.raws.before.length + pseudoNode.sourceIndex, + }) + } + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-pseudo-class-blacklist/README.md b/lib/rules/selector-pseudo-class-blacklist/README.md similarity index 100% rename from src/rules/selector-pseudo-class-blacklist/README.md rename to lib/rules/selector-pseudo-class-blacklist/README.md diff --git a/src/rules/selector-pseudo-class-blacklist/__tests__/index.js b/lib/rules/selector-pseudo-class-blacklist/__tests__/index.js similarity index 86% rename from src/rules/selector-pseudo-class-blacklist/__tests__/index.js rename to lib/rules/selector-pseudo-class-blacklist/__tests__/index.js index 8cdb60ebda..1a74154b65 100644 --- a/src/rules/selector-pseudo-class-blacklist/__tests__/index.js +++ b/lib/rules/selector-pseudo-class-blacklist/__tests__/index.js @@ -1,21 +1,15 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "focus", - "global", - "input-placeholder", - "not", - "nth-last-child", - ]], + config: [[ "focus", "global", "input-placeholder", "not", "nth-last-child" ]], skipBasicChecks: true, accept: [ { @@ -75,9 +69,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "/^last/", - ]], + config: [["/^last/"]], skipBasicChecks: true, accept: [ { diff --git a/lib/rules/selector-pseudo-class-blacklist/index.js b/lib/rules/selector-pseudo-class-blacklist/index.js new file mode 100644 index 0000000000..70fcfa2ab1 --- /dev/null +++ b/lib/rules/selector-pseudo-class-blacklist/index.js @@ -0,0 +1,71 @@ +"use strict" + +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "selector-pseudo-class-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: selector => `Unexpected pseudo-class "${selector}"`, +}) + +const rule = function (blacklist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + const selector = rule.selector + + if (!isStandardSyntaxSelector(selector)) { + return + } + if (selector.indexOf(":") === -1) { + return + } + + parseSelector(selector, result, rule, selectorTree => { + selectorTree.walkPseudos(pseudoNode => { + const value = pseudoNode.value + + // Ignore pseudo-elements + + if (value.slice(0, 2) === "::") { + return + } + + const name = value.slice(1) + + if (!matchesStringOrRegExp(postcss.vendor.unprefixed(name).toLowerCase(), blacklist)) { + return + } + + report({ + index: pseudoNode.sourceIndex, + message: messages.rejected(name), + node: rule, + result, + ruleName, + }) + }) + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-pseudo-class-case/README.md b/lib/rules/selector-pseudo-class-case/README.md similarity index 100% rename from src/rules/selector-pseudo-class-case/README.md rename to lib/rules/selector-pseudo-class-case/README.md diff --git a/src/rules/selector-pseudo-class-case/__tests__/index.js b/lib/rules/selector-pseudo-class-case/__tests__/index.js similarity index 98% rename from src/rules/selector-pseudo-class-case/__tests__/index.js rename to lib/rules/selector-pseudo-class-case/__tests__/index.js index 4cb609b647..73e8829304 100644 --- a/src/rules/selector-pseudo-class-case/__tests__/index.js +++ b/lib/rules/selector-pseudo-class-case/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-pseudo-class-case/index.js b/lib/rules/selector-pseudo-class-case/index.js new file mode 100644 index 0000000000..5cd6222123 --- /dev/null +++ b/lib/rules/selector-pseudo-class-case/index.js @@ -0,0 +1,71 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") + +const ruleName = "selector-pseudo-class-case" + +const messages = ruleMessages(ruleName, { + expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "lower", "upper" ], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + const startIndexPseudo = selector.indexOf(":") + + if (startIndexPseudo === -1) { + return + } + + parseSelector(selector, result, rule, selectorTree => { + selectorTree.walkPseudos(pseudoNode => { + const pseudo = pseudoNode.value + + if (!isStandardSyntaxSelector(pseudo)) { + return + } + + if (pseudo.indexOf("::") !== -1 || keywordSets.levelOneAndTwoPseudoElements.has(pseudo.toLowerCase().slice(1))) { + return + } + + const expectedPseudo = expectation === "lower" ? pseudo.toLowerCase() : pseudo.toUpperCase() + + if (pseudo === expectedPseudo) { + return + } + + report({ + message: messages.expected(pseudo, expectedPseudo), + node: rule, + index: pseudoNode.sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-pseudo-class-no-unknown/README.md b/lib/rules/selector-pseudo-class-no-unknown/README.md similarity index 100% rename from src/rules/selector-pseudo-class-no-unknown/README.md rename to lib/rules/selector-pseudo-class-no-unknown/README.md diff --git a/src/rules/selector-pseudo-class-no-unknown/__tests__/index.js b/lib/rules/selector-pseudo-class-no-unknown/__tests__/index.js similarity index 92% rename from src/rules/selector-pseudo-class-no-unknown/__tests__/index.js rename to lib/rules/selector-pseudo-class-no-unknown/__tests__/index.js index a333d9a06f..9d2e86fff1 100644 --- a/src/rules/selector-pseudo-class-no-unknown/__tests__/index.js +++ b/lib/rules/selector-pseudo-class-no-unknown/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-pseudo-class-no-unknown/index.js b/lib/rules/selector-pseudo-class-no-unknown/index.js new file mode 100644 index 0000000000..07ec31214b --- /dev/null +++ b/lib/rules/selector-pseudo-class-no-unknown/index.js @@ -0,0 +1,83 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const optionsMatches = require("../../utils/optionsMatches") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "selector-pseudo-class-no-unknown" + +const messages = ruleMessages(ruleName, { + rejected: selector => `Unexpected unknown pseudo-class selector "${selector}"`, +}) + +const rule = function (actual, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }, { + actual: options, + possible: { + ignorePseudoClasses: [_.isString], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + + // Return early before parse if no pseudos for performance + + if (selector.indexOf(":") === -1) { + return + } + + parseSelector(selector, result, rule, selectorTree => { + selectorTree.walkPseudos(pseudoNode => { + const value = pseudoNode.value + + if (!isStandardSyntaxSelector(value)) { + return + } + + // Ignore pseudo-elements + if (value.slice(0, 2) === "::") { + return + } + + if (optionsMatches(options, "ignorePseudoClasses", pseudoNode.value.slice(1))) { + return + } + + const name = value.slice(1) + + if (postcss.vendor.prefix(name) || keywordSets.pseudoClasses.has(name.toLowerCase()) || keywordSets.pseudoElements.has(name.toLowerCase())) { + return + } + + report({ + message: messages.rejected(value), + node: rule, + index: pseudoNode.sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-pseudo-class-parentheses-space-inside/README.md b/lib/rules/selector-pseudo-class-parentheses-space-inside/README.md similarity index 100% rename from src/rules/selector-pseudo-class-parentheses-space-inside/README.md rename to lib/rules/selector-pseudo-class-parentheses-space-inside/README.md diff --git a/src/rules/selector-pseudo-class-parentheses-space-inside/__tests__/index.js b/lib/rules/selector-pseudo-class-parentheses-space-inside/__tests__/index.js similarity index 96% rename from src/rules/selector-pseudo-class-parentheses-space-inside/__tests__/index.js rename to lib/rules/selector-pseudo-class-parentheses-space-inside/__tests__/index.js index ecd0c757ba..73b0cb60cd 100644 --- a/src/rules/selector-pseudo-class-parentheses-space-inside/__tests__/index.js +++ b/lib/rules/selector-pseudo-class-parentheses-space-inside/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/selector-pseudo-class-parentheses-space-inside/index.js b/lib/rules/selector-pseudo-class-parentheses-space-inside/index.js similarity index 68% rename from src/rules/selector-pseudo-class-parentheses-space-inside/index.js rename to lib/rules/selector-pseudo-class-parentheses-space-inside/index.js index f6fab5e1fd..4abbf9f535 100644 --- a/src/rules/selector-pseudo-class-parentheses-space-inside/index.js +++ b/lib/rules/selector-pseudo-class-parentheses-space-inside/index.js @@ -1,40 +1,45 @@ -import { - isStandardSyntaxRule, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" -import styleSearch from "style-search" +"use strict" -export const ruleName = "selector-pseudo-class-parentheses-space-inside" +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "selector-pseudo-class-parentheses-space-inside" + +const messages = ruleMessages(ruleName, { expectedOpening: "Expected single space after \"(\"", rejectedOpening: "Unexpected whitespace after \"(\"", expectedClosing: "Expected single space before \")\"", rejectedClosing: "Unexpected whitespace before \")\"", }) -export default function (expectation) { +const rule = function (expectation) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - ], + possible: [ "always", "never" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (rule.selector.indexOf("(") === -1) { return } + if (!isStandardSyntaxRule(rule)) { + return + } + if (rule.selector.indexOf("(") === -1) { + return + } parseSelector(rule.selector, result, rule, selectorTree => { selectorTree.walkPseudos(pseudoNode => { - if (_.get(pseudoNode, "parent.parent.type") === "pseudo") { return } + if (_.get(pseudoNode, "parent.parent.type") === "pseudo") { + return + } const pseudoSelectorString = pseudoNode.toString() @@ -74,3 +79,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-pseudo-class-whitelist/README.md b/lib/rules/selector-pseudo-class-whitelist/README.md similarity index 100% rename from src/rules/selector-pseudo-class-whitelist/README.md rename to lib/rules/selector-pseudo-class-whitelist/README.md diff --git a/src/rules/selector-pseudo-class-whitelist/__tests__/index.js b/lib/rules/selector-pseudo-class-whitelist/__tests__/index.js similarity index 86% rename from src/rules/selector-pseudo-class-whitelist/__tests__/index.js rename to lib/rules/selector-pseudo-class-whitelist/__tests__/index.js index 30ecab540e..f778516204 100644 --- a/src/rules/selector-pseudo-class-whitelist/__tests__/index.js +++ b/lib/rules/selector-pseudo-class-whitelist/__tests__/index.js @@ -1,20 +1,15 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "hover", - "nth-child", - "root", - "placeholder", - ]], + config: [[ "hover", "nth-child", "root", "placeholder" ]], skipBasicChecks: true, accept: [ { @@ -74,9 +69,7 @@ testRule(rule, { testRule(rule, { ruleName, - config: [[ - "/^nth/", - ]], + config: [["/^nth/"]], skipBasicChecks: true, accept: [ { diff --git a/lib/rules/selector-pseudo-class-whitelist/index.js b/lib/rules/selector-pseudo-class-whitelist/index.js new file mode 100644 index 0000000000..7c10ba1d3f --- /dev/null +++ b/lib/rules/selector-pseudo-class-whitelist/index.js @@ -0,0 +1,71 @@ +"use strict" + +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const postcss = require("postcss") + +const ruleName = "selector-pseudo-class-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: selector => `Unexpected pseudo-class "${selector}"`, +}) + +const rule = function (whitelist) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isString], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + const selector = rule.selector + + if (!isStandardSyntaxSelector(selector)) { + return + } + if (selector.indexOf(":") === -1) { + return + } + + parseSelector(selector, result, rule, selectorTree => { + selectorTree.walkPseudos(pseudoNode => { + const value = pseudoNode.value + + // Ignore pseudo-elements + + if (value.slice(0, 2) === "::") { + return + } + + const name = value.slice(1) + + if (matchesStringOrRegExp(postcss.vendor.unprefixed(name).toLowerCase(), whitelist)) { + return + } + + report({ + index: pseudoNode.sourceIndex, + message: messages.rejected(name), + node: rule, + result, + ruleName, + }) + }) + }) + }) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-pseudo-element-case/README.md b/lib/rules/selector-pseudo-element-case/README.md similarity index 100% rename from src/rules/selector-pseudo-element-case/README.md rename to lib/rules/selector-pseudo-element-case/README.md diff --git a/src/rules/selector-pseudo-element-case/__tests__/index.js b/lib/rules/selector-pseudo-element-case/__tests__/index.js similarity index 98% rename from src/rules/selector-pseudo-element-case/__tests__/index.js rename to lib/rules/selector-pseudo-element-case/__tests__/index.js index 071b7ec4ca..4fc5b33b22 100644 --- a/src/rules/selector-pseudo-element-case/__tests__/index.js +++ b/lib/rules/selector-pseudo-element-case/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] @@ -398,4 +398,3 @@ testRule(rule, { code: "a::#{$variable} {}", } ], }) - diff --git a/lib/rules/selector-pseudo-element-case/index.js b/lib/rules/selector-pseudo-element-case/index.js new file mode 100644 index 0000000000..e7b8c11654 --- /dev/null +++ b/lib/rules/selector-pseudo-element-case/index.js @@ -0,0 +1,71 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") + +const ruleName = "selector-pseudo-element-case" + +const messages = ruleMessages(ruleName, { + expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "lower", "upper" ], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + const startIndexPseudoElement = selector.indexOf(":") + + if (startIndexPseudoElement === -1) { + return + } + + parseSelector(selector, result, rule, selectorTree => { + selectorTree.walkPseudos(pseudoNode => { + const pseudoElement = pseudoNode.value + + if (!isStandardSyntaxSelector(pseudoElement)) { + return + } + + if (pseudoElement.indexOf("::") === -1 && !keywordSets.levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase().slice(1))) { + return + } + + const expectedPseudoElement = expectation === "lower" ? pseudoElement.toLowerCase() : pseudoElement.toUpperCase() + + if (pseudoElement === expectedPseudoElement) { + return + } + + report({ + message: messages.expected(pseudoElement, expectedPseudoElement), + node: rule, + index: pseudoNode.sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-pseudo-element-colon-notation/README.md b/lib/rules/selector-pseudo-element-colon-notation/README.md similarity index 100% rename from src/rules/selector-pseudo-element-colon-notation/README.md rename to lib/rules/selector-pseudo-element-colon-notation/README.md diff --git a/src/rules/selector-pseudo-element-colon-notation/__tests__/index.js b/lib/rules/selector-pseudo-element-colon-notation/__tests__/index.js similarity index 93% rename from src/rules/selector-pseudo-element-colon-notation/__tests__/index.js rename to lib/rules/selector-pseudo-element-colon-notation/__tests__/index.js index bdbd49bcf3..b7c5e0b174 100644 --- a/src/rules/selector-pseudo-element-colon-notation/__tests__/index.js +++ b/lib/rules/selector-pseudo-element-colon-notation/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-pseudo-element-colon-notation/index.js b/lib/rules/selector-pseudo-element-colon-notation/index.js new file mode 100644 index 0000000000..8516e67db1 --- /dev/null +++ b/lib/rules/selector-pseudo-element-colon-notation/index.js @@ -0,0 +1,64 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const keywordSets = require("../../reference/keywordSets") +const styleSearch = require("style-search") + +const ruleName = "selector-pseudo-element-colon-notation" + +const messages = ruleMessages(ruleName, { + expected: q => `Expected ${q} colon pseudo-element notation`, +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "single", "double" ], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + + // get out early if no pseudo elements or classes + if (selector.indexOf(":") === -1) { + return + } + + // match only level 1 and 2 pseudo elements + const pseudoElementsWithColons = _.toArray(keywordSets.levelOneAndTwoPseudoElements).map(x => `:${x}`) + styleSearch({ source: selector.toLowerCase(), target: pseudoElementsWithColons }, match => { + const prevCharIsColon = selector[match.startIndex - 1] === ":" + + if (expectation === "single" && !prevCharIsColon) { + return + } + if (expectation === "double" && prevCharIsColon) { + return + } + + report({ + message: messages.expected(expectation), + node: rule, + index: match.startIndex, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-pseudo-element-no-unknown/README.md b/lib/rules/selector-pseudo-element-no-unknown/README.md similarity index 100% rename from src/rules/selector-pseudo-element-no-unknown/README.md rename to lib/rules/selector-pseudo-element-no-unknown/README.md diff --git a/src/rules/selector-pseudo-element-no-unknown/__tests__/index.js b/lib/rules/selector-pseudo-element-no-unknown/__tests__/index.js similarity index 93% rename from src/rules/selector-pseudo-element-no-unknown/__tests__/index.js rename to lib/rules/selector-pseudo-element-no-unknown/__tests__/index.js index f0235e38dd..8d873687a2 100644 --- a/src/rules/selector-pseudo-element-no-unknown/__tests__/index.js +++ b/lib/rules/selector-pseudo-element-no-unknown/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-pseudo-element-no-unknown/index.js b/lib/rules/selector-pseudo-element-no-unknown/index.js new file mode 100644 index 0000000000..a830c985ca --- /dev/null +++ b/lib/rules/selector-pseudo-element-no-unknown/index.js @@ -0,0 +1,83 @@ +"use strict" + +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const optionsMatches = require("../../utils/optionsMatches") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const keywordSets = require("../../reference/keywordSets") +const postcss = require("postcss") + +const ruleName = "selector-pseudo-element-no-unknown" + +const messages = ruleMessages(ruleName, { + rejected: selector => `Unexpected unknown pseudo-element selector "${selector}"`, +}) + +const rule = function (actual, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }, { + actual: options, + possible: { + ignorePseudoElements: [_.isString], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + + // Return early before parse if no pseudos for performance + + if (selector.indexOf(":") === -1) { + return + } + + parseSelector(selector, result, rule, selectorTree => { + selectorTree.walkPseudos(pseudoNode => { + const value = pseudoNode.value + + if (!isStandardSyntaxSelector(value)) { + return + } + + // Ignore pseudo-classes + if (value.slice(0, 2) !== "::") { + return + } + + if (optionsMatches(options, "ignorePseudoElements", pseudoNode.value.slice(2))) { + return + } + + const name = value.slice(2) + + if (postcss.vendor.prefix(name) || keywordSets.pseudoElements.has(name.toLowerCase())) { + return + } + + report({ + message: messages.rejected(value), + node: rule, + index: pseudoNode.sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-root-no-composition/README.md b/lib/rules/selector-root-no-composition/README.md similarity index 100% rename from src/rules/selector-root-no-composition/README.md rename to lib/rules/selector-root-no-composition/README.md diff --git a/src/rules/selector-root-no-composition/__tests__/index.js b/lib/rules/selector-root-no-composition/__tests__/index.js similarity index 80% rename from src/rules/selector-root-no-composition/__tests__/index.js rename to lib/rules/selector-root-no-composition/__tests__/index.js index c372d98eec..8fa8bfcf33 100644 --- a/src/rules/selector-root-no-composition/__tests__/index.js +++ b/lib/rules/selector-root-no-composition/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-root-no-composition/index.js b/lib/rules/selector-root-no-composition/index.js new file mode 100644 index 0000000000..4c2fc9f783 --- /dev/null +++ b/lib/rules/selector-root-no-composition/index.js @@ -0,0 +1,37 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-root-no-composition" + +const messages = ruleMessages(ruleName, { + rejected: "Unexpected composition", +}) + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + if (rule.selector.toLowerCase().indexOf(":root") === -1 || rule.selector.toLowerCase().trim() === ":root") { + return + } + + report({ + message: messages.rejected, + node: rule, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-type-case/README.md b/lib/rules/selector-type-case/README.md similarity index 100% rename from src/rules/selector-type-case/README.md rename to lib/rules/selector-type-case/README.md diff --git a/src/rules/selector-type-case/__tests__/index.js b/lib/rules/selector-type-case/__tests__/index.js similarity index 94% rename from src/rules/selector-type-case/__tests__/index.js rename to lib/rules/selector-type-case/__tests__/index.js index 18fe0a9316..a839213272 100644 --- a/src/rules/selector-type-case/__tests__/index.js +++ b/lib/rules/selector-type-case/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-type-case/index.js b/lib/rules/selector-type-case/index.js new file mode 100644 index 0000000000..47b63f6edf --- /dev/null +++ b/lib/rules/selector-type-case/index.js @@ -0,0 +1,72 @@ +"use strict" + +const isKeyframeSelector = require("../../utils/isKeyframeSelector") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const isStandardSyntaxTypeSelector = require("../../utils/isStandardSyntaxTypeSelector") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") + +const ruleName = "selector-type-case" + +const messages = ruleMessages(ruleName, { + expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "lower", "upper" ], + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + const selector = rule.selector, + selectors = rule.selectors + + if (!isStandardSyntaxRule(rule)) { + return + } + if (!isStandardSyntaxSelector(selector)) { + return + } + if (selectors.some(s => isKeyframeSelector(s))) { + return + } + + parseSelector(selector, result, rule, selectorAST => { + selectorAST.walkTags(tag => { + if (!isStandardSyntaxTypeSelector(tag)) { + return + } + + const sourceIndex = tag.sourceIndex, + value = tag.value + + const expectedValue = expectation === "lower" ? value.toLowerCase() : value.toUpperCase() + + if (value === expectedValue) { + return + } + + report({ + message: messages.expected(value, expectedValue), + node: rule, + index: sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/selector-type-no-unknown/README.md b/lib/rules/selector-type-no-unknown/README.md similarity index 100% rename from src/rules/selector-type-no-unknown/README.md rename to lib/rules/selector-type-no-unknown/README.md diff --git a/src/rules/selector-type-no-unknown/__tests__/index.js b/lib/rules/selector-type-no-unknown/__tests__/index.js similarity index 94% rename from src/rules/selector-type-no-unknown/__tests__/index.js rename to lib/rules/selector-type-no-unknown/__tests__/index.js index de549107b9..fa00e583da 100644 --- a/src/rules/selector-type-no-unknown/__tests__/index.js +++ b/lib/rules/selector-type-no-unknown/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/selector-type-no-unknown/index.js b/lib/rules/selector-type-no-unknown/index.js new file mode 100644 index 0000000000..0cdad25a15 --- /dev/null +++ b/lib/rules/selector-type-no-unknown/index.js @@ -0,0 +1,84 @@ +"use strict" + +const isKeyframeSelector = require("../../utils/isKeyframeSelector") +const isStandardSyntaxRule = require("../../utils/isStandardSyntaxRule") +const isStandardSyntaxSelector = require("../../utils/isStandardSyntaxSelector") +const isStandardSyntaxTypeSelector = require("../../utils/isStandardSyntaxTypeSelector") +const optionsMatches = require("../../utils/optionsMatches") +const parseSelector = require("../../utils/parseSelector") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const htmlTags = require("html-tags") +const _ = require("lodash") +const svgTags = require("svg-tags") + +const ruleName = "selector-type-no-unknown" + +const messages = ruleMessages(ruleName, { + rejected: selector => `Unexpected unknown type selector "${selector}"`, +}) + +// htmlTags includes only "standard" tags. So we augment it with older tags etc. +const nonStandardHtmlTags = new Set([ "acronym", "applet", "basefont", "big", "blink", "center", "content", "dir", "font", "frame", "frameset", "hgroup", "isindex", "keygen", "listing", "marquee", "noembed", "plaintext", "spacer", "strike", "tt", "xmp" ]) + +const rule = function (actual, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }, { + actual: options, + possible: { + ignoreTypes: [_.isString], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkRules(rule => { + const selector = rule.selector, + selectors = rule.selectors + + if (!isStandardSyntaxRule(rule)) { + return + } + if (!isStandardSyntaxSelector(selector)) { + return + } + if (selectors.some(s => isKeyframeSelector(s))) { + return + } + + parseSelector(selector, result, rule, selectorTree => { + selectorTree.walkTags(tagNode => { + if (!isStandardSyntaxTypeSelector(tagNode)) { + return + } + + if (optionsMatches(options, "ignoreTypes", tagNode.value)) { + return + } + + const tagName = tagNode.value + const tagNameLowerCase = tagName.toLowerCase() + + if (htmlTags.indexOf(tagNameLowerCase) !== -1 || svgTags.indexOf(tagNameLowerCase) !== -1 || nonStandardHtmlTags.has(tagNameLowerCase)) { + return + } + + report({ + message: messages.rejected(tagName), + node: rule, + index: tagNode.sourceIndex, + ruleName, + result, + }) + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/lib/rules/selectorAttributeOperatorSpaceChecker.js b/lib/rules/selectorAttributeOperatorSpaceChecker.js new file mode 100644 index 0000000000..413b0db351 --- /dev/null +++ b/lib/rules/selectorAttributeOperatorSpaceChecker.js @@ -0,0 +1,48 @@ +"use strict" + +const isStandardSyntaxRule = require("../utils/isStandardSyntaxRule") +const parseSelector = require("../utils/parseSelector") +const report = require("../utils/report") +const styleSearch = require("style-search") + +module.exports = function (options) { + options.root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + if (rule.selector.indexOf("[") === -1 || rule.selector.indexOf("=") === -1) { + return + } + + parseSelector(rule.selector, options.result, rule, selectorTree => { + selectorTree.walkAttributes(attributeNode => { + const operator = attributeNode.operator + + if (!operator) { + return + } + + const attributeNodeString = attributeNode.toString() + + styleSearch({ source: attributeNodeString, target: operator }, match => { + const index = options.checkBeforeOperator ? match.startIndex : match.endIndex - 1 + checkOperator(attributeNodeString, index, rule, attributeNode.sourceIndex, operator) + }) + }) + }) + + function checkOperator(source, index, node, attributeIndex, operator) { + options.locationChecker({ + source, + index, + err: m => report({ + message: m.replace(options.checkBeforeOperator ? operator[0] : operator[operator.length - 1], operator), + node, + index: attributeIndex + index, + result: options.result, + ruleName: options.checkedRuleName, + }), + }) + } + }) +} diff --git a/lib/rules/selectorCombinatorSpaceChecker.js b/lib/rules/selectorCombinatorSpaceChecker.js new file mode 100644 index 0000000000..a292f394da --- /dev/null +++ b/lib/rules/selectorCombinatorSpaceChecker.js @@ -0,0 +1,48 @@ +"use strict" + +const report = require("../utils/report") +const _ = require("lodash") +const punctuationSets = require("../reference/punctuationSets") +const styleSearch = require("style-search") + +module.exports = function (opts) { + opts.root.walkRules(rule => { + // Check each selector individually, instead of all as one string, + // in case some that aren't the first begin with combinators (nesting syntax) + rule.selectors.forEach(selector => { + styleSearch({ + source: selector, + target: _.toArray(punctuationSets.nonSpaceCombinators), + parentheticals: "skip", + }, match => { + const endIndex = match.endIndex, + startIndex = match.startIndex, + target = match.target + + // Catch ~= in attribute selectors + + if (target === "~" && selector[endIndex] === "=") { + return + } + + // Catch escaped combinator-like character + if (selector[startIndex - 1] === "\\") { + return + } + + check(selector, startIndex, rule) + }) + }) + }) + + function check(source, index, node) { + opts.locationChecker({ source, index, err: m => report({ + message: m, + node, + index, + result: opts.result, + ruleName: opts.checkedRuleName, + }), + }) + } +} diff --git a/lib/rules/selectorListCommaWhitespaceChecker.js b/lib/rules/selectorListCommaWhitespaceChecker.js new file mode 100644 index 0000000000..271119ce8c --- /dev/null +++ b/lib/rules/selectorListCommaWhitespaceChecker.js @@ -0,0 +1,32 @@ +"use strict" + +const isStandardSyntaxRule = require("../utils/isStandardSyntaxRule") +const report = require("../utils/report") +const styleSearch = require("style-search") + +module.exports = function (opts) { + opts.root.walkRules(rule => { + if (!isStandardSyntaxRule(rule)) { + return + } + const selector = rule.selector + styleSearch({ + source: selector, + target: ",", + functionArguments: "skip", + }, match => { + checkDelimiter(selector, match.startIndex, rule) + }) + }) + + function checkDelimiter(source, index, node) { + opts.locationChecker({ source, index, err: m => report({ + message: m, + node, + index, + result: opts.result, + ruleName: opts.checkedRuleName, + }), + }) + } +} diff --git a/src/rules/shorthand-property-no-redundant-values/README.md b/lib/rules/shorthand-property-no-redundant-values/README.md similarity index 100% rename from src/rules/shorthand-property-no-redundant-values/README.md rename to lib/rules/shorthand-property-no-redundant-values/README.md diff --git a/src/rules/shorthand-property-no-redundant-values/__tests__/index.js b/lib/rules/shorthand-property-no-redundant-values/__tests__/index.js similarity index 97% rename from src/rules/shorthand-property-no-redundant-values/__tests__/index.js rename to lib/rules/shorthand-property-no-redundant-values/__tests__/index.js index f219c60b5f..a9e384216e 100644 --- a/src/rules/shorthand-property-no-redundant-values/__tests__/index.js +++ b/lib/rules/shorthand-property-no-redundant-values/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/shorthand-property-no-redundant-values/index.js b/lib/rules/shorthand-property-no-redundant-values/index.js new file mode 100644 index 0000000000..5430e45c88 --- /dev/null +++ b/lib/rules/shorthand-property-no-redundant-values/index.js @@ -0,0 +1,122 @@ +"use strict" + +const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration") +const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const shorthandData = require("../../reference/shorthandData") +const valueParser = require("postcss-value-parser") +const postcss = require("postcss") + +const ruleName = "shorthand-property-no-redundant-values" + +const messages = ruleMessages(ruleName, { + rejected: (unexpected, expected) => `Unexpected longhand value '${unexpected}' instead of '${expected}'`, +}) + +const shorthandableProperties = new Set(Object.keys(shorthandData)) + +const ignoredCharacters = [ "+", "-", "*", "/", "(", ")", "$", "@", "--", "var(" ] + +const ignoredShorthandProperties = new Set([ "background", "font", "border", "border-top", "border-bottom", "border-left", "border-right", "list-style", "transition" ]) + +function isIgnoredCharacters(value) { + return ignoredCharacters.some(char => value.indexOf(char) !== -1) +} + +function canCondense(top, right) { + const bottom = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null + const left = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null + + const lowerTop = top.toLowerCase() + const lowerRight = right.toLowerCase() + const lowerBottom = bottom && bottom.toLowerCase() + const lowerLeft = left && left.toLowerCase() + + if (canCondenseToOneValue(lowerTop, lowerRight, lowerBottom, lowerLeft)) { + return [top] + } else if (canCondenseToTwoValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) { + return [ top, right ] + } else if (canCondenseToThreeValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) { + return [ top, right, bottom ] + } else { + return [ top, right, bottom, left ] + } +} + +function canCondenseToOneValue(top, right, bottom, left) { + if (top !== right) { + return false + } + + return top === bottom && (bottom === left || !left) || !bottom && !left +} + +function canCondenseToTwoValues(top, right, bottom, left) { + return top === bottom && right === left || top === bottom && !left && top !== right +} + +function canCondenseToThreeValues(top, right, bottom, left) { + return right === left +} + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + if (!isStandardSyntaxDeclaration(decl) || !isStandardSyntaxProperty(decl.prop)) { + return + } + + const prop = decl.prop, + value = decl.value + + const normalizedProp = postcss.vendor.unprefixed(prop.toLowerCase()) + + // Ignore not shorthandable properties, and math operations + if (isIgnoredCharacters(value) || !shorthandableProperties.has(normalizedProp) || ignoredShorthandProperties.has(normalizedProp)) { + return + } + + const valuesToShorthand = [] + + valueParser(value).walk(valueNode => { + if (valueNode.type !== "word") { + return + } + + valuesToShorthand.push(valueParser.stringify(valueNode)) + }) + + if (valuesToShorthand.length <= 1 || valuesToShorthand.length > 4) { + return + } + + const shortestForm = canCondense.apply(undefined, valuesToShorthand) + const shortestFormString = shortestForm.filter(value => { + return value + }).join(" ") + const valuesFormString = valuesToShorthand.join(" ") + + if (shortestFormString.toLowerCase() === valuesFormString.toLowerCase()) { + return + } + + report({ + message: messages.rejected(value, shortestFormString), + node: decl, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/string-no-newline/README.md b/lib/rules/string-no-newline/README.md similarity index 100% rename from src/rules/string-no-newline/README.md rename to lib/rules/string-no-newline/README.md diff --git a/src/rules/string-no-newline/__tests__/index.js b/lib/rules/string-no-newline/__tests__/index.js similarity index 85% rename from src/rules/string-no-newline/__tests__/index.js rename to lib/rules/string-no-newline/__tests__/index.js index 69230a0d0a..56e51230eb 100644 --- a/src/rules/string-no-newline/__tests__/index.js +++ b/lib/rules/string-no-newline/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/string-no-newline/index.js b/lib/rules/string-no-newline/index.js similarity index 52% rename from src/rules/string-no-newline/index.js rename to lib/rules/string-no-newline/index.js index 41cce4af3e..446946bcf7 100644 --- a/src/rules/string-no-newline/index.js +++ b/lib/rules/string-no-newline/index.js @@ -1,20 +1,22 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" +"use strict" -export const ruleName = "string-no-newline" +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") -export const messages = ruleMessages(ruleName, { +const ruleName = "string-no-newline" + +const messages = ruleMessages(ruleName, { rejected: "Unexpected newline in string", }) -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } const cssString = root.toString() styleSearch({ @@ -24,7 +26,9 @@ export default function (actual) { }, match => { const charBefore = cssString[match.startIndex - 1] let index = match.startIndex - if (charBefore === "\\") { return } + if (charBefore === "\\") { + return + } if (charBefore === "\r") index -= 1 report({ message: messages.rejected, @@ -36,3 +40,7 @@ export default function (actual) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/string-quotes/README.md b/lib/rules/string-quotes/README.md similarity index 100% rename from src/rules/string-quotes/README.md rename to lib/rules/string-quotes/README.md diff --git a/src/rules/string-quotes/__tests__/index.js b/lib/rules/string-quotes/__tests__/index.js similarity index 94% rename from src/rules/string-quotes/__tests__/index.js rename to lib/rules/string-quotes/__tests__/index.js index ca40c23d48..c9892fbb1d 100644 --- a/src/rules/string-quotes/__tests__/index.js +++ b/lib/rules/string-quotes/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/string-quotes/index.js b/lib/rules/string-quotes/index.js new file mode 100644 index 0000000000..bbca0e0e9f --- /dev/null +++ b/lib/rules/string-quotes/index.js @@ -0,0 +1,41 @@ +"use strict" + +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") + +const ruleName = "string-quotes" + +const messages = ruleMessages(ruleName, { + expected: q => `Expected ${q} quotes`, +}) + +const rule = function (expectation) { + const erroneousQuote = expectation === "single" ? "\"" : "'" + + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "single", "double" ], + }) + if (!validOptions) { + return + } + + const cssString = root.toString() + styleSearch({ source: cssString, target: erroneousQuote }, match => { + report({ + message: messages.expected(expectation), + node: root, + index: match.startIndex, + result, + ruleName, + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/stylelint-disable-reason/README.md b/lib/rules/stylelint-disable-reason/README.md similarity index 100% rename from src/rules/stylelint-disable-reason/README.md rename to lib/rules/stylelint-disable-reason/README.md diff --git a/src/rules/stylelint-disable-reason/__tests__/index.js b/lib/rules/stylelint-disable-reason/__tests__/index.js similarity index 92% rename from src/rules/stylelint-disable-reason/__tests__/index.js rename to lib/rules/stylelint-disable-reason/__tests__/index.js index cff39a2d9e..b5f5482c7a 100644 --- a/src/rules/stylelint-disable-reason/__tests__/index.js +++ b/lib/rules/stylelint-disable-reason/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/stylelint-disable-reason/index.js b/lib/rules/stylelint-disable-reason/index.js similarity index 66% rename from src/rules/stylelint-disable-reason/index.js rename to lib/rules/stylelint-disable-reason/index.js index 38a3337e4c..4032844257 100644 --- a/src/rules/stylelint-disable-reason/index.js +++ b/lib/rules/stylelint-disable-reason/index.js @@ -1,12 +1,12 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" +"use strict" -export const ruleName = "stylelint-disable-reason" +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") -export const messages = ruleMessages(ruleName, { +const ruleName = "stylelint-disable-reason" + +const messages = ruleMessages(ruleName, { expectedBefore: "Expected comment reason before `stylelint-disable` comment", expectedAfter: "Expected comment reason after `stylelint-disable` comment", }) @@ -14,37 +14,31 @@ export const messages = ruleMessages(ruleName, { const stylelintDisableCommand = "stylelint-disable" const stylelintDisableLineCommand = "stylelint-disable-line" -export default function (expectation) { +const rule = function (expectation) { return function (root, result) { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always-before", - "always-after", - ], + possible: [ "always-before", "always-after" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkComments(function (comment) { - if (comment.text.indexOf(stylelintDisableCommand) !== 0) { return } + if (comment.text.indexOf(stylelintDisableCommand) !== 0) { + return + } if (expectation === "always-before") { const prev = comment.prev() - const prevIsCommentAndValid = prev - && prev.type === "comment" - && !isDisableCommand(prev.text) + const prevIsCommentAndValid = prev && prev.type === "comment" && !isDisableCommand(prev.text) let prevDisableLineIsCommentAndValid = false - if (comment.text.indexOf(stylelintDisableLineCommand) === 0 - && !prevIsCommentAndValid - && prev - ) { + if (comment.text.indexOf(stylelintDisableLineCommand) === 0 && !prevIsCommentAndValid && prev) { const friendlyPrev = prev.prev() - prevDisableLineIsCommentAndValid = friendlyPrev - && friendlyPrev.type === "comment" - && !isDisableCommand(friendlyPrev.text) + prevDisableLineIsCommentAndValid = friendlyPrev && friendlyPrev.type === "comment" && !isDisableCommand(friendlyPrev.text) } if (!prevIsCommentAndValid && !prevDisableLineIsCommentAndValid) { @@ -61,9 +55,7 @@ export default function (expectation) { } } else if (expectation === "always-after") { const next = comment.next() - const nextIsCommentAndValid = next - && next.type === "comment" - && !isDisableCommand(next.text) + const nextIsCommentAndValid = next && next.type === "comment" && !isDisableCommand(next.text) if (!nextIsCommentAndValid) { const disabledRanges = result.stylelint.disabledRanges @@ -85,3 +77,7 @@ export default function (expectation) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/time-no-imperceptible/README.md b/lib/rules/time-no-imperceptible/README.md similarity index 100% rename from src/rules/time-no-imperceptible/README.md rename to lib/rules/time-no-imperceptible/README.md diff --git a/src/rules/time-no-imperceptible/__tests__/index.js b/lib/rules/time-no-imperceptible/__tests__/index.js similarity index 96% rename from src/rules/time-no-imperceptible/__tests__/index.js rename to lib/rules/time-no-imperceptible/__tests__/index.js index 524a8ea4e4..5d876e3203 100644 --- a/src/rules/time-no-imperceptible/__tests__/index.js +++ b/lib/rules/time-no-imperceptible/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/time-no-imperceptible/index.js b/lib/rules/time-no-imperceptible/index.js similarity index 50% rename from src/rules/time-no-imperceptible/index.js rename to lib/rules/time-no-imperceptible/index.js index 7644d8bd71..46830b3189 100644 --- a/src/rules/time-no-imperceptible/index.js +++ b/lib/rules/time-no-imperceptible/index.js @@ -1,37 +1,36 @@ -import { - declarationValueIndex, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - longhandTimeProperties, - shorthandTimeProperties, -} from "../../reference/keywordSets" -import postcss from "postcss" -import valueParser from "postcss-value-parser" - -export const ruleName = "time-no-imperceptible" - -export const messages = ruleMessages(ruleName, { +"use strict" + +const declarationValueIndex = require("../../utils/declarationValueIndex") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const keywordSets = require("../../reference/keywordSets") +const postcss = require("postcss") +const valueParser = require("postcss-value-parser") + +const ruleName = "time-no-imperceptible" + +const messages = ruleMessages(ruleName, { rejected: time => `Unexpected time value "${time}" less than or equal to 100ms`, }) const MINIMUM_MILLISECONDS = 100 -export default function (actual) { +const rule = function (actual) { return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } + if (!validOptions) { + return + } root.walkDecls(decl => { - if (longhandTimeProperties.has(postcss.vendor.unprefixed(decl.prop.toLowerCase()))) { + if (keywordSets.longhandTimeProperties.has(postcss.vendor.unprefixed(decl.prop.toLowerCase()))) { if (isImperceptibleTime(decl.value)) { complain(messages.rejected(decl.value), decl) } } - if (shorthandTimeProperties.has(postcss.vendor.unprefixed(decl.prop.toLowerCase()))) { + if (keywordSets.shorthandTimeProperties.has(postcss.vendor.unprefixed(decl.prop.toLowerCase()))) { const valueList = postcss.list.space(decl.value) for (const value of valueList) { if (isImperceptibleTime(value)) { @@ -45,12 +44,18 @@ export default function (actual) { const parsedTime = valueParser.unit(time) if (!parsedTime) return false const absoluteTime = Math.abs(parsedTime.number) - if (parsedTime.unit.toLowerCase() === "ms" && absoluteTime <= MINIMUM_MILLISECONDS) { return true } - if (parsedTime.unit.toLowerCase() === "s" && absoluteTime * 1000 <= MINIMUM_MILLISECONDS) { return true } + if (parsedTime.unit.toLowerCase() === "ms" && absoluteTime <= MINIMUM_MILLISECONDS) { + return true + } + if (parsedTime.unit.toLowerCase() === "s" && absoluteTime * 1000 <= MINIMUM_MILLISECONDS) { + return true + } return false } - function complain(message, decl, offset = 0) { + function complain(message, decl) { + const offset = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 0 + report({ result, ruleName, @@ -61,3 +66,7 @@ export default function (actual) { } } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/unit-blacklist/README.md b/lib/rules/unit-blacklist/README.md similarity index 100% rename from src/rules/unit-blacklist/README.md rename to lib/rules/unit-blacklist/README.md diff --git a/src/rules/unit-blacklist/__tests__/index.js b/lib/rules/unit-blacklist/__tests__/index.js similarity index 93% rename from src/rules/unit-blacklist/__tests__/index.js rename to lib/rules/unit-blacklist/__tests__/index.js index 18198fba2c..4a25408e9e 100644 --- a/src/rules/unit-blacklist/__tests__/index.js +++ b/lib/rules/unit-blacklist/__tests__/index.js @@ -1,19 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "px", - "vmin", - ]], + config: [[ "px", "vmin" ]], accept: [ { code: "a { line-height: 1; }", @@ -200,15 +197,12 @@ testRule(rule, { testRule(rule, { ruleName, - config: [ - [ "px", "vmin" ], - { - ignoreProperties: { - "px": [ "font-size", "margin", "/^border/" ], - "vmin": [ "width", "height" ], - }, + config: [ [ "px", "vmin" ], { + ignoreProperties: { + "px": [ "font-size", "margin", "/^border/" ], + "vmin": [ "width", "height" ], }, - ], + } ], accept: [ { code: "a { font-size: 13px; }", diff --git a/lib/rules/unit-blacklist/index.js b/lib/rules/unit-blacklist/index.js new file mode 100644 index 0000000000..d07e355b0a --- /dev/null +++ b/lib/rules/unit-blacklist/index.js @@ -0,0 +1,73 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const declarationValueIndex = require("../../utils/declarationValueIndex") +const getUnitFromValueNode = require("../../utils/getUnitFromValueNode") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const validateObjectWithStringArrayProps = require("../../utils/validateObjectWithStringArrayProps") +const valueParser = require("postcss-value-parser") + +const ruleName = "unit-blacklist" + +const messages = ruleMessages(ruleName, { + rejected: unit => `Unexpected unit "${unit}"`, +}) + +const rule = function (blacklistInput, options) { + const blacklist = [].concat(blacklistInput) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: blacklist, + possible: [_.isString], + }, { + optional: true, + actual: options, + possible: { + ignoreProperties: validateObjectWithStringArrayProps, + }, + }) + if (!validOptions) { + return + } + + function check(node, value, getIndex) { + valueParser(value).walk(function (valueNode) { + // Ignore wrong units within `url` function + if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { + return false + } + + const unit = getUnitFromValueNode(valueNode) + + if (!unit || unit && blacklist.indexOf(unit.toLowerCase()) === -1) { + return + } + + if (options && optionsMatches(options.ignoreProperties, unit.toLowerCase(), node.prop)) { + return + } + + report({ + index: getIndex(node) + valueNode.sourceIndex, + message: messages.rejected(unit), + node, + result, + ruleName, + }) + }) + } + + root.walkAtRules(/^media$/i, atRule => check(atRule, atRule.params, atRuleParamIndex)) + root.walkDecls(decl => check(decl, decl.value, declarationValueIndex)) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/unit-case/README.md b/lib/rules/unit-case/README.md similarity index 100% rename from src/rules/unit-case/README.md rename to lib/rules/unit-case/README.md diff --git a/src/rules/unit-case/__tests__/index.js b/lib/rules/unit-case/__tests__/index.js similarity index 98% rename from src/rules/unit-case/__tests__/index.js rename to lib/rules/unit-case/__tests__/index.js index ecea2ce2ff..e79a8d389f 100644 --- a/src/rules/unit-case/__tests__/index.js +++ b/lib/rules/unit-case/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/unit-case/index.js b/lib/rules/unit-case/index.js new file mode 100644 index 0000000000..30db5bd21c --- /dev/null +++ b/lib/rules/unit-case/index.js @@ -0,0 +1,63 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const declarationValueIndex = require("../../utils/declarationValueIndex") +const getUnitFromValueNode = require("../../utils/getUnitFromValueNode") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const valueParser = require("postcss-value-parser") + +const ruleName = "unit-case" + +const messages = ruleMessages(ruleName, { + expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, +}) + +const rule = function (expectation) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "lower", "upper" ], + }) + if (!validOptions) { + return + } + + function check(node, value, getIndex) { + valueParser(value).walk(valueNode => { + // Ignore wrong units within `url` function + if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { + return false + } + + const unit = getUnitFromValueNode(valueNode) + + if (!unit) { + return + } + + const expectedUnit = expectation === "lower" ? unit.toLowerCase() : unit.toUpperCase() + + if (unit === expectedUnit) { + return + } + + report({ + index: getIndex(node) + valueNode.sourceIndex, + message: messages.expected(unit, expectedUnit), + node, + result, + ruleName, + }) + }) + } + + root.walkAtRules(/^media$/i, atRule => check(atRule, atRule.params, atRuleParamIndex)) + root.walkDecls(decl => check(decl, decl.value, declarationValueIndex)) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/unit-no-unknown/README.md b/lib/rules/unit-no-unknown/README.md similarity index 100% rename from src/rules/unit-no-unknown/README.md rename to lib/rules/unit-no-unknown/README.md diff --git a/src/rules/unit-no-unknown/__tests__/index.js b/lib/rules/unit-no-unknown/__tests__/index.js similarity index 97% rename from src/rules/unit-no-unknown/__tests__/index.js rename to lib/rules/unit-no-unknown/__tests__/index.js index 579972a884..cb93ca0096 100644 --- a/src/rules/unit-no-unknown/__tests__/index.js +++ b/lib/rules/unit-no-unknown/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/unit-no-unknown/index.js b/lib/rules/unit-no-unknown/index.js new file mode 100644 index 0000000000..cfe23d5551 --- /dev/null +++ b/lib/rules/unit-no-unknown/index.js @@ -0,0 +1,71 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const declarationValueIndex = require("../../utils/declarationValueIndex") +const getUnitFromValueNode = require("../../utils/getUnitFromValueNode") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const keywordSets = require("../../reference/keywordSets") +const valueParser = require("postcss-value-parser") + +const ruleName = "unit-no-unknown" + +const messages = ruleMessages(ruleName, { + rejected: unit => `Unexpected unknown unit "${unit}"`, +}) + +const rule = function (actual, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }, { + actual: options, + possible: { + ignoreUnits: [_.isString], + }, + optional: true, + }) + + if (!validOptions) { + return + } + + function check(node, value, getIndex) { + valueParser(value).walk(function (valueNode) { + // Ignore wrong units within `url` function + if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { + return false + } + + const unit = getUnitFromValueNode(valueNode) + if (!unit) { + return + } + + if (optionsMatches(options, "ignoreUnits", unit)) { + return + } + + if (keywordSets.units.has(unit.toLowerCase())) { + return + } + + report({ + index: getIndex(node) + valueNode.sourceIndex, + message: messages.rejected(unit), + node, + result, + ruleName, + }) + }) + } + + root.walkAtRules(/^media$/i, atRule => check(atRule, atRule.params, atRuleParamIndex)) + root.walkDecls(decl => check(decl, decl.value, declarationValueIndex)) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/unit-whitelist/README.md b/lib/rules/unit-whitelist/README.md similarity index 100% rename from src/rules/unit-whitelist/README.md rename to lib/rules/unit-whitelist/README.md diff --git a/src/rules/unit-whitelist/__tests__/index.js b/lib/rules/unit-whitelist/__tests__/index.js similarity index 93% rename from src/rules/unit-whitelist/__tests__/index.js rename to lib/rules/unit-whitelist/__tests__/index.js index f03bca852f..0bae56fae3 100644 --- a/src/rules/unit-whitelist/__tests__/index.js +++ b/lib/rules/unit-whitelist/__tests__/index.js @@ -1,19 +1,16 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] testRule(rule, { ruleName, - config: [[ - "px", - "em", - ]], + config: [[ "px", "em" ]], accept: [ { code: "a { line-height: 1; }", @@ -198,15 +195,12 @@ testRule(rule, { testRule(rule, { ruleName, - config: [ - [ "px", "em" ], - { - ignoreProperties: { - "rem": [ "line-height", "margin", "/^border/" ], - "%": [ "width", "height" ], - }, + config: [ [ "px", "em" ], { + ignoreProperties: { + "rem": [ "line-height", "margin", "/^border/" ], + "%": [ "width", "height" ], }, - ], + } ], accept: [ { code: "a { line-height: 0.1rem; }", diff --git a/lib/rules/unit-whitelist/index.js b/lib/rules/unit-whitelist/index.js new file mode 100644 index 0000000000..8617e3f2cd --- /dev/null +++ b/lib/rules/unit-whitelist/index.js @@ -0,0 +1,73 @@ +"use strict" + +const atRuleParamIndex = require("../../utils/atRuleParamIndex") +const declarationValueIndex = require("../../utils/declarationValueIndex") +const getUnitFromValueNode = require("../../utils/getUnitFromValueNode") +const optionsMatches = require("../../utils/optionsMatches") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const validateObjectWithStringArrayProps = require("../../utils/validateObjectWithStringArrayProps") +const valueParser = require("postcss-value-parser") + +const ruleName = "unit-whitelist" + +const messages = ruleMessages(ruleName, { + rejected: unit => `Unexpected unit "${unit}"`, +}) + +const rule = function (whitelistInput, options) { + const whitelist = [].concat(whitelistInput) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: whitelist, + possible: [_.isString], + }, { + optional: true, + actual: options, + possible: { + ignoreProperties: validateObjectWithStringArrayProps, + }, + }) + if (!validOptions) { + return + } + + function check(node, value, getIndex) { + valueParser(value).walk(function (valueNode) { + // Ignore wrong units within `url` function + if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { + return false + } + + const unit = getUnitFromValueNode(valueNode) + + if (!unit || unit && whitelist.indexOf(unit.toLowerCase()) !== -1) { + return + } + + if (options && optionsMatches(options["ignoreProperties"], unit.toLowerCase(), node.prop)) { + return + } + + report({ + index: getIndex(node) + valueNode.sourceIndex, + message: messages.rejected(unit), + node, + result, + ruleName, + }) + }) + } + + root.walkAtRules(/^media$/i, atRule => check(atRule, atRule.params, atRuleParamIndex)) + root.walkDecls(decl => check(decl, decl.value, declarationValueIndex)) + } +} + +rule.primaryOptionArray = true + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/value-keyword-case/README.md b/lib/rules/value-keyword-case/README.md similarity index 100% rename from src/rules/value-keyword-case/README.md rename to lib/rules/value-keyword-case/README.md diff --git a/src/rules/value-keyword-case/__tests__/index.js b/lib/rules/value-keyword-case/__tests__/index.js similarity index 99% rename from src/rules/value-keyword-case/__tests__/index.js rename to lib/rules/value-keyword-case/__tests__/index.js index fbfc3c846d..ea13cff104 100644 --- a/src/rules/value-keyword-case/__tests__/index.js +++ b/lib/rules/value-keyword-case/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/value-keyword-case/index.js b/lib/rules/value-keyword-case/index.js new file mode 100644 index 0000000000..c83a6acb21 --- /dev/null +++ b/lib/rules/value-keyword-case/index.js @@ -0,0 +1,135 @@ +"use strict" + +const keywordSets = require("../../reference/keywordSets") +const declarationValueIndex = require("../../utils/declarationValueIndex") +const getUnitFromValueNode = require("../../utils/getUnitFromValueNode") +const isCounterIncrementCustomIdentValue = require("../../utils/isCounterIncrementCustomIdentValue") +const isStandardSyntaxValue = require("../../utils/isStandardSyntaxValue") +const matchesStringOrRegExp = require("../../utils/matchesStringOrRegExp") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const _ = require("lodash") +const valueParser = require("postcss-value-parser") + +const ruleName = "value-keyword-case" + +const messages = ruleMessages(ruleName, { + expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, +}) + +// Operators are interpreted as "words" by the value parser, so we want to make sure to ignore them. +const ignoredCharacters = new Set([ "+", "-", "/", "*", "%" ]) + +const mapLowercaseKeywordsToCamelCase = new Map() +keywordSets.camelCaseKeywords.forEach(func => { + mapLowercaseKeywordsToCamelCase.set(func.toLowerCase(), func) +}) + +const rule = function (expectation, options) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "lower", "upper" ], + }, { + actual: options, + possible: { + ignoreKeywords: [_.isString], + }, + optional: true, + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const prop = decl.prop, + value = decl.value + + valueParser(value).walk(node => { + const valueLowerCase = node.value.toLowerCase() + + // Ignore system colors + if (keywordSets.systemColors.has(valueLowerCase)) { + return + } + + // Ignore keywords within `url` and `var` function + if (node.type === "function" && (valueLowerCase === "url" || valueLowerCase === "var")) { + return false + } + + const keyword = node.value + + // Ignore css variables, and hex values, and math operators, and sass interpolation + if (node.type !== "word" || !isStandardSyntaxValue(node.value) || value.indexOf("#") !== -1 || ignoredCharacters.has(keyword) || getUnitFromValueNode(node)) { + return + } + + if (prop === "animation" && !keywordSets.animationShorthandKeywords.has(valueLowerCase) && !keywordSets.animationNameKeywords.has(valueLowerCase)) { + return + } + if (prop === "animation-name" && !keywordSets.animationNameKeywords.has(valueLowerCase)) { + return + } + if (prop === "font" && !keywordSets.fontShorthandKeywords.has(valueLowerCase) && !keywordSets.fontFamilyKeywords.has(valueLowerCase)) { + return + } + if (prop === "font-family" && !keywordSets.fontFamilyKeywords.has(valueLowerCase)) { + return + } + if (prop === "counter-increment" && isCounterIncrementCustomIdentValue(valueLowerCase)) { + return + } + if (prop === "grid-row" && !keywordSets.gridRowKeywords.has(valueLowerCase)) { + return + } + if (prop === "grid-column" && !keywordSets.gridColumnKeywords.has(valueLowerCase)) { + return + } + if (prop === "grid-area" && !keywordSets.gridAreaKeywords.has(valueLowerCase)) { + return + } + if (prop === "list-style" && !keywordSets.listStyleShorthandKeywords.has(valueLowerCase) && !keywordSets.listStyleTypeKeywords.has(valueLowerCase)) { + return + } + if (prop === "list-style-type" && !keywordSets.listStyleTypeKeywords.has(valueLowerCase)) { + return + } + + const ignoreKeywords = options && options.ignoreKeywords || [] + + if (ignoreKeywords.length > 0 && matchesStringOrRegExp(keyword, ignoreKeywords)) { + return + } + + const keywordLowerCase = keyword.toLocaleLowerCase() + let expectedKeyword = null + + if (expectation === "lower" && mapLowercaseKeywordsToCamelCase.has(keywordLowerCase)) { + expectedKeyword = mapLowercaseKeywordsToCamelCase.get(keywordLowerCase) + } else if (expectation === "lower") { + expectedKeyword = keyword.toLowerCase() + } else { + expectedKeyword = keyword.toUpperCase() + } + + if (keyword === expectedKeyword) { + return + } + + report({ + message: messages.expected(keyword, expectedKeyword), + node: decl, + index: declarationValueIndex(decl) + node.sourceIndex, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/value-list-comma-newline-after/README.md b/lib/rules/value-list-comma-newline-after/README.md similarity index 100% rename from src/rules/value-list-comma-newline-after/README.md rename to lib/rules/value-list-comma-newline-after/README.md diff --git a/src/rules/value-list-comma-newline-after/__tests__/index.js b/lib/rules/value-list-comma-newline-after/__tests__/index.js similarity index 94% rename from src/rules/value-list-comma-newline-after/__tests__/index.js rename to lib/rules/value-list-comma-newline-after/__tests__/index.js index ad0081f4fc..cc487b24bb 100644 --- a/src/rules/value-list-comma-newline-after/__tests__/index.js +++ b/lib/rules/value-list-comma-newline-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/value-list-comma-newline-after/index.js b/lib/rules/value-list-comma-newline-after/index.js new file mode 100644 index 0000000000..6f9be6aed0 --- /dev/null +++ b/lib/rules/value-list-comma-newline-after/index.js @@ -0,0 +1,38 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const valueListCommaWhitespaceChecker = require("../valueListCommaWhitespaceChecker") + +const ruleName = "value-list-comma-newline-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected newline after \",\"", + expectedAfterMultiLine: () => "Expected newline after \",\" in a multi-line list", + rejectedAfterMultiLine: () => "Unexpected whitespace after \",\" in a multi-line list", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("newline", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "always-multi-line", "never-multi-line" ], + }) + if (!validOptions) { + return + } + + valueListCommaWhitespaceChecker({ + root, + result, + locationChecker: checker.afterOneOnly, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/value-list-comma-newline-before/README.md b/lib/rules/value-list-comma-newline-before/README.md similarity index 100% rename from src/rules/value-list-comma-newline-before/README.md rename to lib/rules/value-list-comma-newline-before/README.md diff --git a/src/rules/value-list-comma-newline-before/__tests__/index.js b/lib/rules/value-list-comma-newline-before/__tests__/index.js similarity index 95% rename from src/rules/value-list-comma-newline-before/__tests__/index.js rename to lib/rules/value-list-comma-newline-before/__tests__/index.js index efbccc0fdc..44041a9767 100644 --- a/src/rules/value-list-comma-newline-before/__tests__/index.js +++ b/lib/rules/value-list-comma-newline-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/value-list-comma-newline-before/index.js b/lib/rules/value-list-comma-newline-before/index.js new file mode 100644 index 0000000000..c5b46d4408 --- /dev/null +++ b/lib/rules/value-list-comma-newline-before/index.js @@ -0,0 +1,38 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const valueListCommaWhitespaceChecker = require("../valueListCommaWhitespaceChecker") + +const ruleName = "value-list-comma-newline-before" + +const messages = ruleMessages(ruleName, { + expectedBefore: () => "Expected newline before \",\"", + expectedBeforeMultiLine: () => "Expected newline before \",\" in a multi-line list", + rejectedBeforeMultiLine: () => "Unexpected whitespace before \",\" in a multi-line list", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("newline", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "always-multi-line", "never-multi-line" ], + }) + if (!validOptions) { + return + } + + valueListCommaWhitespaceChecker({ + root, + result, + locationChecker: checker.beforeAllowingIndentation, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/value-list-comma-space-after/README.md b/lib/rules/value-list-comma-space-after/README.md similarity index 100% rename from src/rules/value-list-comma-space-after/README.md rename to lib/rules/value-list-comma-space-after/README.md diff --git a/src/rules/value-list-comma-space-after/__tests__/index.js b/lib/rules/value-list-comma-space-after/__tests__/index.js similarity index 96% rename from src/rules/value-list-comma-space-after/__tests__/index.js rename to lib/rules/value-list-comma-space-after/__tests__/index.js index fcf7511e22..866f057fa4 100644 --- a/src/rules/value-list-comma-space-after/__tests__/index.js +++ b/lib/rules/value-list-comma-space-after/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/value-list-comma-space-after/index.js b/lib/rules/value-list-comma-space-after/index.js new file mode 100644 index 0000000000..2fb1ccb6fb --- /dev/null +++ b/lib/rules/value-list-comma-space-after/index.js @@ -0,0 +1,39 @@ +"use strict" + +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const valueListCommaWhitespaceChecker = require("../valueListCommaWhitespaceChecker") + +const ruleName = "value-list-comma-space-after" + +const messages = ruleMessages(ruleName, { + expectedAfter: () => "Expected single space after \",\"", + rejectedAfter: () => "Unexpected whitespace after \",\"", + expectedAfterSingleLine: () => "Expected single space after \",\" in a single-line list", + rejectedAfterSingleLine: () => "Unexpected whitespace after \",\" in a single-line list", +}) + +const rule = function (expectation) { + const checker = whitespaceChecker("space", expectation, messages) + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: expectation, + possible: [ "always", "never", "always-single-line", "never-single-line" ], + }) + if (!validOptions) { + return + } + + valueListCommaWhitespaceChecker({ + root, + result, + locationChecker: checker.after, + checkedRuleName: ruleName, + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/value-list-comma-space-before/README.md b/lib/rules/value-list-comma-space-before/README.md similarity index 100% rename from src/rules/value-list-comma-space-before/README.md rename to lib/rules/value-list-comma-space-before/README.md diff --git a/src/rules/value-list-comma-space-before/__tests__/index.js b/lib/rules/value-list-comma-space-before/__tests__/index.js similarity index 96% rename from src/rules/value-list-comma-space-before/__tests__/index.js rename to lib/rules/value-list-comma-space-before/__tests__/index.js index d49e8660fd..1f48024737 100644 --- a/src/rules/value-list-comma-space-before/__tests__/index.js +++ b/lib/rules/value-list-comma-space-before/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/src/rules/value-list-comma-space-before/index.js b/lib/rules/value-list-comma-space-before/index.js similarity index 51% rename from src/rules/value-list-comma-space-before/index.js rename to lib/rules/value-list-comma-space-before/index.js index c613e2d902..85fa5efd5b 100644 --- a/src/rules/value-list-comma-space-before/index.js +++ b/lib/rules/value-list-comma-space-before/index.js @@ -1,32 +1,29 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { valueListCommaWhitespaceChecker } from "../value-list-comma-space-after" +"use strict" -export const ruleName = "value-list-comma-space-before" +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const whitespaceChecker = require("../../utils/whitespaceChecker") +const valueListCommaWhitespaceChecker = require("../valueListCommaWhitespaceChecker") -export const messages = ruleMessages(ruleName, { +const ruleName = "value-list-comma-space-before" + +const messages = ruleMessages(ruleName, { expectedBefore: () => "Expected single space before \",\"", rejectedBefore: () => "Unexpected whitespace before \",\"", expectedBeforeSingleLine: () => "Unexpected whitespace before \",\" in a single-line list", rejectedBeforeSingleLine: () => "Unexpected whitespace before \",\" in a single-line list", }) -export default function (expectation) { +const rule = function (expectation) { const checker = whitespaceChecker("space", expectation, messages) return (root, result) => { const validOptions = validateOptions(result, ruleName, { actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], + possible: [ "always", "never", "always-single-line", "never-single-line" ], }) - if (!validOptions) { return } + if (!validOptions) { + return + } valueListCommaWhitespaceChecker({ root, @@ -36,3 +33,7 @@ export default function (expectation) { }) } } + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/value-list-max-empty-lines/README.md b/lib/rules/value-list-max-empty-lines/README.md similarity index 100% rename from src/rules/value-list-max-empty-lines/README.md rename to lib/rules/value-list-max-empty-lines/README.md diff --git a/src/rules/value-list-max-empty-lines/__tests__/index.js b/lib/rules/value-list-max-empty-lines/__tests__/index.js similarity index 94% rename from src/rules/value-list-max-empty-lines/__tests__/index.js rename to lib/rules/value-list-max-empty-lines/__tests__/index.js index 128eeebe89..4dd2475908 100644 --- a/src/rules/value-list-max-empty-lines/__tests__/index.js +++ b/lib/rules/value-list-max-empty-lines/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/value-list-max-empty-lines/index.js b/lib/rules/value-list-max-empty-lines/index.js new file mode 100644 index 0000000000..69faf5bf1b --- /dev/null +++ b/lib/rules/value-list-max-empty-lines/index.js @@ -0,0 +1,55 @@ +"use strict" + +const _ = require("lodash") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") + +const ruleName = "value-list-max-empty-lines" + +const messages = ruleMessages(ruleName, { + expected: max => `Expected no more than ${max} empty line(s)`, +}) + +const rule = function (max) { + const maxAdjacentNewlines = max + 1 + + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { + actual: max, + possible: _.isNumber, + }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + const value = decl.value + const repeatLFNewLines = _.repeat("\n", maxAdjacentNewlines) + const repeatCRLFNewLines = _.repeat("\r\n", maxAdjacentNewlines) + + styleSearch({ source: value, target: "\n" }, match => { + if (value.substr(match.startIndex + 1, maxAdjacentNewlines) === repeatLFNewLines || value.substr(match.startIndex + 1, maxAdjacentNewlines * 2) === repeatCRLFNewLines) { + // Put index at `\r` if it's CRLF, otherwise leave it at `\n` + let index = match.startIndex + if (value[index - 1] === "\r") { + index -= 1 + } + + report({ + message: messages.expected(max), + node: decl, + index, + result, + ruleName, + }) + } + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/src/rules/value-no-vendor-prefix/README.md b/lib/rules/value-no-vendor-prefix/README.md similarity index 100% rename from src/rules/value-no-vendor-prefix/README.md rename to lib/rules/value-no-vendor-prefix/README.md diff --git a/src/rules/value-no-vendor-prefix/__tests__/index.js b/lib/rules/value-no-vendor-prefix/__tests__/index.js similarity index 92% rename from src/rules/value-no-vendor-prefix/__tests__/index.js rename to lib/rules/value-no-vendor-prefix/__tests__/index.js index 897d864273..26b39ec5d8 100644 --- a/src/rules/value-no-vendor-prefix/__tests__/index.js +++ b/lib/rules/value-no-vendor-prefix/__tests__/index.js @@ -1,9 +1,9 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" +"use strict" + +const messages = require("..").messages +const ruleName = require("..").ruleName +const rules = require("../../../rules") +const testRule = require("../../../testUtils/testRule") const rule = rules[ruleName] diff --git a/lib/rules/value-no-vendor-prefix/index.js b/lib/rules/value-no-vendor-prefix/index.js new file mode 100644 index 0000000000..980a15235b --- /dev/null +++ b/lib/rules/value-no-vendor-prefix/index.js @@ -0,0 +1,56 @@ +"use strict" + +const isAutoprefixable = require("../../utils/isAutoprefixable") +const isStandardSyntaxDeclaration = require("../../utils/isStandardSyntaxDeclaration") +const isStandardSyntaxProperty = require("../../utils/isStandardSyntaxProperty") +const report = require("../../utils/report") +const ruleMessages = require("../../utils/ruleMessages") +const validateOptions = require("../../utils/validateOptions") +const styleSearch = require("style-search") + +const ruleName = "value-no-vendor-prefix" + +const messages = ruleMessages(ruleName, { + rejected: value => `Unexpected vendor-prefix "${value}"`, +}) + +const valuePrefixes = [ "-webkit-", "-moz-", "-ms-", "-o-" ] + +const rule = function (actual) { + return (root, result) => { + const validOptions = validateOptions(result, ruleName, { actual }) + if (!validOptions) { + return + } + + root.walkDecls(decl => { + if (!isStandardSyntaxDeclaration(decl) || !isStandardSyntaxProperty(decl.prop) || decl.value[0] !== "-") { + return + } + + const prop = decl.prop, + value = decl.value + + // Search the full declaration in order to get an accurate index + + styleSearch({ source: value.toLowerCase(), target: valuePrefixes }, match => { + const fullIdentifier = /^(-[a-z-]+)\b/i.exec(value.slice(match.startIndex))[1] + if (!isAutoprefixable.propertyValue(prop, fullIdentifier)) { + return + } + + report({ + message: messages.rejected(fullIdentifier), + node: decl, + index: prop.length + decl.raws.between.length + match.startIndex, + result, + ruleName, + }) + }) + }) + } +} + +rule.ruleName = ruleName +rule.messages = messages +module.exports = rule diff --git a/lib/rules/valueListCommaWhitespaceChecker.js b/lib/rules/valueListCommaWhitespaceChecker.js new file mode 100644 index 0000000000..6e80cdb377 --- /dev/null +++ b/lib/rules/valueListCommaWhitespaceChecker.js @@ -0,0 +1,37 @@ +"use strict" + +const isStandardSyntaxDeclaration = require("../utils/isStandardSyntaxDeclaration") +const isStandardSyntaxProperty = require("../utils/isStandardSyntaxProperty") +const report = require("../utils/report") +const styleSearch = require("style-search") + +module.exports = function (opts) { + opts.root.walkDecls(decl => { + if (!isStandardSyntaxDeclaration(decl) || !isStandardSyntaxProperty(decl.prop)) { + return + } + styleSearch({ + source: decl.toString(), + target: ",", + functionArguments: "skip", + }, match => { + checkComma(decl.toString(), match.startIndex, decl) + }) + }) + + function checkComma(source, index, node) { + opts.locationChecker({ + source, + index, + err: m => { + report({ + message: m, + node, + index, + result: opts.result, + ruleName: opts.checkedRuleName, + }) + }, + }) + } +} diff --git a/lib/standalone.js b/lib/standalone.js new file mode 100644 index 0000000000..d283cd0f15 --- /dev/null +++ b/lib/standalone.js @@ -0,0 +1,127 @@ +/* @flow */ +"use strict" +const formatters = require("./formatters") +const createStylelint = require("./createStylelint") +const globby = require("globby") +const needlessDisables = require("./needlessDisables") + +const alwaysIgnoredGlobs = [ "!**/node_modules/**", "!**/bower_components/**" ] + +module.exports = function (options/*: Object */)/*: Promise*/ { + const files = options.files + const code = options.code + const codeFilename = options.codeFilename + const config = options.config + const configFile = options.configFile + const configBasedir = options.configBasedir + const configOverrides = options.configOverrides + const ignoreDisables = options.ignoreDisables + const ignorePath = options.ignorePath + const reportNeedlessDisables = options.reportNeedlessDisables + const formatter = options.formatter + const syntax = options.syntax + const customSyntax = options.customSyntax + const allowEmptyInput = options.allowEmptyInput + + const isValidCode = typeof code === "string" + if (!files && !isValidCode || files && (code || isValidCode)) { + throw new Error("You must pass stylelint a `files` glob or a `code` string, though not both") + } + + let formatterFunction/*: Function*/ + if (typeof formatter === "string") { + formatterFunction = formatters[formatter] + if (formatterFunction === undefined) { + return Promise.reject(new Error("You must use a valid formatter option: 'json', 'string', 'verbose', or a function")) + } + } else if (typeof formatter === "function") { + formatterFunction = formatter + } else { + formatterFunction = formatters.json + } + + const stylelint = createStylelint({ + config, + configFile, + configBasedir, + configOverrides, + ignoreDisables, + ignorePath, + reportNeedlessDisables, + syntax, + customSyntax, + }) + + if (!files) { + return stylelint._lintSource({ code, codeFilename }).then(postcssResult => { + return stylelint._createStylelintResult(postcssResult) + }).catch(handleError).then(stylelintResult => { + return prepareReturnValue([stylelintResult]) + }) + } + + return globby([].concat(files, alwaysIgnoredGlobs)).then(filePaths => { + if (!filePaths.length) { + if (allowEmptyInput === undefined || !allowEmptyInput) { + const err/*: Object*/ = new Error("Files glob patterns specified did not match any files") + err.code = 80 + throw err + } else { + return Promise.all([]) + } + } + + const getStylelintResults = filePaths.map(filePath => { + return stylelint._lintSource({ filePath }).then(postcssResult => { + return stylelint._createStylelintResult(postcssResult, filePath) + }).catch(handleError) + }) + + return Promise.all(getStylelintResults) + }).then(prepareReturnValue) + + function prepareReturnValue(stylelintResults/*: Array*/)/*: stylelint$standaloneReturnValue*/ { + const errored = stylelintResults.some(result => result.errored) + const returnValue/*: stylelint$standaloneReturnValue*/ = { + errored, + output: formatterFunction(stylelintResults), + results: stylelintResults, + } + if (reportNeedlessDisables) { + returnValue.needlessDisables = needlessDisables(stylelintResults) + } + return returnValue + } +} + +function handleError(error) { + if (error.name === "CssSyntaxError") { + return convertCssSyntaxErrorToResult(error) + } else { + throw error + } +} + +// By converting syntax errors to stylelint results, +// we can control their appearance in the formatted output +// and other tools like editor plugins can decide how to +// present them, as well +function convertCssSyntaxErrorToResult(error/*: Object*/)/*: stylelint$result*/ { + if (error.name !== "CssSyntaxError") { + throw error + } + + return { + source: error.file || "", + deprecations: [], + invalidOptionWarnings: [], + errored: true, + warnings: [{ + line: error.line, + column: error.column, + rule: error.name, + severity: "error", + text: error.reason + " (" + error.name + ")", + }], + } +} diff --git a/src/testUtils/__tests__/mergeTestDescriptions-test.js b/lib/testUtils/__tests__/mergeTestDescriptions-test.js similarity index 89% rename from src/testUtils/__tests__/mergeTestDescriptions-test.js rename to lib/testUtils/__tests__/mergeTestDescriptions-test.js index f357006932..9ea4728f4b 100644 --- a/src/testUtils/__tests__/mergeTestDescriptions-test.js +++ b/lib/testUtils/__tests__/mergeTestDescriptions-test.js @@ -1,5 +1,7 @@ -import { mergeTestDescriptions } from "../" -import test from "tape" +"use strict" + +const mergeTestDescriptions = require("../mergeTestDescriptions") +const test = require("tape") test("merge objects", t => { t.deepEqual(mergeTestDescriptions({}, {}), {}, "empty object") diff --git a/lib/testUtils/basicChecks.js b/lib/testUtils/basicChecks.js new file mode 100644 index 0000000000..bfe4c34c25 --- /dev/null +++ b/lib/testUtils/basicChecks.js @@ -0,0 +1,16 @@ +"use strict" + +// These should pass for *almost* every rule +module.exports = [ { + code: "", + description: "empty stylesheet", +}, { + code: "a {}", + description: "empty rule", +}, { + code: "@import \"foo.css\";", + description: "blockless statement", +}, { + code: ":global {}", + description: "CSS Modules global empty rule set", +} ] diff --git a/src/testUtils/createRuleTester.js b/lib/testUtils/createRuleTester.js similarity index 90% rename from src/testUtils/createRuleTester.js rename to lib/testUtils/createRuleTester.js index 1bc48b831b..439f5dd8c7 100644 --- a/src/testUtils/createRuleTester.js +++ b/lib/testUtils/createRuleTester.js @@ -1,11 +1,13 @@ -import _ from "lodash" -import assignDisabledRanges from "../assignDisabledRanges" -import basicChecks from "./basicChecks" -import lessSyntax from "postcss-less" -import normalizeRuleSettings from "../normalizeRuleSettings" -import postcss from "postcss" -import scssSyntax from "postcss-scss" -import sugarss from "sugarss" +"use strict" + +const _ = require("lodash") +const assignDisabledRanges = require("../assignDisabledRanges") +const basicChecks = require("./basicChecks") +const lessSyntax = require("postcss-less") +const normalizeRuleSettings = require("../normalizeRuleSettings") +const postcss = require("postcss") +const scssSyntax = require("postcss-scss") +const sugarss = require("sugarss") /** * Create a stylelint rule testing function. @@ -82,13 +84,17 @@ import sugarss from "sugarss" let onlyTest function checkCaseForOnly(caseType, testCase) { - if (!testCase.only) { return } + if (!testCase.only) { + return + } /* istanbul ignore next */ - if (onlyTest) { throw new Error("Cannot use `only` on multiple test cases") } + if (onlyTest) { + throw new Error("Cannot use `only` on multiple test cases") + } onlyTest = { case: testCase, type: caseType } } -export default function (equalityCheck) { +module.exports = function (equalityCheck) { return function (rule, schema) { const alreadyHadOnlyTest = !!onlyTest if (schema.accept) { @@ -115,12 +121,13 @@ export default function (equalityCheck) { } function processGroup(rule, schema, equalityCheck) { - const { ruleName } = schema + const ruleName = schema.ruleName + const ruleOptions = normalizeRuleSettings(schema.config) const rulePrimaryOptions = ruleOptions[0] const ruleSecondaryOptions = ruleOptions[1] - let printableConfig = (rulePrimaryOptions) ? JSON.stringify(rulePrimaryOptions) : "" + let printableConfig = rulePrimaryOptions ? JSON.stringify(rulePrimaryOptions) : "" if (printableConfig && ruleSecondaryOptions) { printableConfig += ", " + JSON.stringify(ruleSecondaryOptions) } @@ -156,19 +163,18 @@ function processGroup(rule, schema, equalityCheck) { schema.preceedingPlugins.forEach(processor.use) } - return processor.use(rule(rulePrimaryOptions, ruleSecondaryOptions)) - .process(code, postcssProcessOptions) + return processor.use(rule(rulePrimaryOptions, ruleSecondaryOptions)).process(code, postcssProcessOptions) } // Apply the basic positive checks unless // explicitly told not to - const passingTestCases = (schema.skipBasicChecks) - ? schema.accept - : basicChecks.concat(schema.accept) + const passingTestCases = schema.skipBasicChecks ? schema.accept : basicChecks.concat(schema.accept) if (passingTestCases && passingTestCases.length) { passingTestCases.forEach(acceptedCase => { - if (!acceptedCase) { return } + if (!acceptedCase) { + return + } const assertionDescription = spaceJoin(acceptedCase.description, "should be accepted") const resultPromise = postcssProcess(acceptedCase.code).then(postcssResult => { const warnings = postcssResult.warnings() @@ -248,6 +254,6 @@ function processGroup(rule, schema, equalityCheck) { } } -function spaceJoin(...args) { - return _.compact(args).join(" ") +function spaceJoin() { + return _.compact(Array.from(arguments)).join(" ") } diff --git a/lib/testUtils/mergeTestDescriptions.js b/lib/testUtils/mergeTestDescriptions.js new file mode 100644 index 0000000000..0cc4023619 --- /dev/null +++ b/lib/testUtils/mergeTestDescriptions.js @@ -0,0 +1,17 @@ +"use strict" + +const _ = require("lodash") + +module.exports = function () { + const mergeWithArgs = [{}] + Array.from(arguments).forEach((arg) => mergeWithArgs.push(arg)) + mergeWithArgs.push(mergeCustomizer) + + return _.mergeWith.apply(_, mergeWithArgs) +} + +function mergeCustomizer(objValue, srcValue) { + if (_.isArray(objValue, mergeCustomizer)) { + return objValue.concat(srcValue) + } +} diff --git a/lib/testUtils/testRule.js b/lib/testUtils/testRule.js new file mode 100644 index 0000000000..8650dfc29d --- /dev/null +++ b/lib/testUtils/testRule.js @@ -0,0 +1,20 @@ +"use strict" + +const createRuleTester = require("./createRuleTester") +const test = require("tape") + +// This code is included here instead of using stylelint-test-rule-tape +// because tests performs significantly faster this way +function assertEquality(processCss, context) { + const testFn = context.only ? test.only : test + testFn(context.caseDescription, t => { + t.plan(context.comparisonCount) + processCss.then(comparisons => { + comparisons.forEach((comparison) => { + t.equal(comparison.actual, comparison.expected, comparison.description) + }) + }) + }) +} + +module.exports = createRuleTester(assertEquality) diff --git a/src/utils/__tests__/atRuleParamIndex-test.js b/lib/utils/__tests__/atRuleParamIndex-test.js similarity index 80% rename from src/utils/__tests__/atRuleParamIndex-test.js rename to lib/utils/__tests__/atRuleParamIndex-test.js index 39ed468b8b..07d18a83e7 100644 --- a/src/utils/__tests__/atRuleParamIndex-test.js +++ b/lib/utils/__tests__/atRuleParamIndex-test.js @@ -1,6 +1,8 @@ -import atRuleParamIndex from "../atRuleParamIndex" -import postcss from "postcss" -import test from "tape" +"use strict" + +const atRuleParamIndex = require("../atRuleParamIndex") +const postcss = require("postcss") +const test = require("tape") test("atRuleParamIndex", t => { t.plan(4) diff --git a/src/utils/__tests__/beforeBlockString-test.js b/lib/utils/__tests__/beforeBlockString-test.js similarity index 64% rename from src/utils/__tests__/beforeBlockString-test.js rename to lib/utils/__tests__/beforeBlockString-test.js index feeaffdbef..bd2fca3d05 100644 --- a/src/utils/__tests__/beforeBlockString-test.js +++ b/lib/utils/__tests__/beforeBlockString-test.js @@ -1,7 +1,9 @@ -import beforeBlockString from "../beforeBlockString" -import postcss from "postcss" -import sugarss from "sugarss" -import test from "tape" +"use strict" + +const beforeBlockString = require("../beforeBlockString") +const postcss = require("postcss") +const sugarss = require("sugarss") +const test = require("tape") test("beforeBlockString rules", t => { t.equal(postcssCheck("a {}"), "a ") @@ -14,15 +16,7 @@ test("beforeBlockString at-rules", t => { t.equal(postcssCheck("@media print {}"), "@media print ") t.equal(postcssCheck("\n@media print, screen\n\t{}"), "\n@media print, screen\n\t") t.equal(postcssCheck("@supports (animation-name: test) {}"), "@supports (animation-name: test) ") - t.equal(postcssCheck( - "@document url(http://www.w3.org/),\n " + - "url-prefix(http://www.w3.org/Style/),\n" + - "domain(mozilla.org),\n" + - "regexp(\"https:.*\") {}"), - "@document url(http://www.w3.org/),\n " + - "url-prefix(http://www.w3.org/Style/),\n" + - "domain(mozilla.org),\n" + - "regexp(\"https:.*\") ") + t.equal(postcssCheck("@document url(http://www.w3.org/),\n " + "url-prefix(http://www.w3.org/Style/),\n" + "domain(mozilla.org),\n" + "regexp(\"https:.*\") {}"), "@document url(http://www.w3.org/),\n " + "url-prefix(http://www.w3.org/Style/),\n" + "domain(mozilla.org),\n" + "regexp(\"https:.*\") ") t.end() }) @@ -47,7 +41,11 @@ test("beforeBlockString without brackets using SugarSS parser", t => { t.end() }) -function postcssCheck(options = {}, cssString, parser = postcss) { +function postcssCheck() { + const options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {} + let cssString = arguments[1] + const parser = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : postcss + if (typeof options === "string") { cssString = options } diff --git a/src/utils/__tests__/blockString-test.js b/lib/utils/__tests__/blockString-test.js similarity index 61% rename from src/utils/__tests__/blockString-test.js rename to lib/utils/__tests__/blockString-test.js index 43d2071708..34ef7b13f8 100644 --- a/src/utils/__tests__/blockString-test.js +++ b/lib/utils/__tests__/blockString-test.js @@ -1,6 +1,8 @@ -import blockString from "../blockString" -import postcss from "postcss" -import test from "tape" +"use strict" + +const blockString = require("../blockString") +const postcss = require("postcss") +const test = require("tape") test("blockString rules", t => { t.equal(postcssCheck("a { color: pink; }"), "{ color: pink; }") @@ -10,9 +12,7 @@ test("blockString rules", t => { test("blockString at-rules", t => { t.equal(postcssCheck("@media print { a { color: pink; } }"), "{ a { color: pink; } }") - t.equal( - postcssCheck("@keyframes foo {\n 0% {\n top: 0;\n}\n\n 100% {\n top: 10px;\n}\n}\n"), - "{\n 0% {\n top: 0;\n}\n\n 100% {\n top: 10px;\n}\n}") + t.equal(postcssCheck("@keyframes foo {\n 0% {\n top: 0;\n}\n\n 100% {\n top: 10px;\n}\n}\n"), "{\n 0% {\n top: 0;\n}\n\n 100% {\n top: 10px;\n}\n}") t.end() }) diff --git a/src/utils/__tests__/blurComments-test.js b/lib/utils/__tests__/blurComments-test.js similarity index 67% rename from src/utils/__tests__/blurComments-test.js rename to lib/utils/__tests__/blurComments-test.js index 8c1296bc08..937760e186 100644 --- a/src/utils/__tests__/blurComments-test.js +++ b/lib/utils/__tests__/blurComments-test.js @@ -1,5 +1,7 @@ -import blurComments from "../blurComments" -import test from "tape" +"use strict" + +const blurComments = require("../blurComments") +const test = require("tape") test("blurComments", t => { t.equal(blurComments("abc"), "abc") diff --git a/src/utils/__tests__/blurFunctionArguments-test.js b/lib/utils/__tests__/blurFunctionArguments-test.js similarity index 80% rename from src/utils/__tests__/blurFunctionArguments-test.js rename to lib/utils/__tests__/blurFunctionArguments-test.js index d74a2fa81c..a1640bd19e 100644 --- a/src/utils/__tests__/blurFunctionArguments-test.js +++ b/lib/utils/__tests__/blurFunctionArguments-test.js @@ -1,5 +1,7 @@ -import blurFunctionArguments from "../blurFunctionArguments" -import test from "tape" +"use strict" + +const blurFunctionArguments = require("../blurFunctionArguments") +const test = require("tape") test("blurFunctionArguments", t => { t.equal(blurFunctionArguments("abc abc", "url"), "abc abc") diff --git a/src/utils/__tests__/blurInterpolation-test.js b/lib/utils/__tests__/blurInterpolation-test.js similarity index 59% rename from src/utils/__tests__/blurInterpolation-test.js rename to lib/utils/__tests__/blurInterpolation-test.js index 9004ec183f..ea487b6dd4 100644 --- a/src/utils/__tests__/blurInterpolation-test.js +++ b/lib/utils/__tests__/blurInterpolation-test.js @@ -1,5 +1,7 @@ -import blurInterpolation from "../blurInterpolation" -import test from "tape" +"use strict" + +const blurInterpolation = require("../blurInterpolation") +const test = require("tape") test("blurInterpolation", t => { t.equal(blurInterpolation("#{$selector}"), " $selector ") @@ -8,10 +10,7 @@ test("blurInterpolation", t => { t.equal(blurInterpolation("#{$font-size}/#{$line-height}"), " $font-size / $line-height ") t.equal(blurInterpolation("url(#{$selector * 10px})"), "url( $selector * 10px )") t.equal(blurInterpolation("calc(#{$selector} * 2)"), "calc( $selector * 2)") - t.equal(blurInterpolation( - "filter: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}');"), - "filter: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr=' ie-hex-str($green) ', endColorstr=' ie-hex-str($translucent-red) ');" - ) + t.equal(blurInterpolation("filter: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}');"), "filter: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr=' ie-hex-str($green) ', endColorstr=' ie-hex-str($translucent-red) ');") t.equal(blurInterpolation("\"I ate #{5 + 10} pies!\""), "\"I ate 5 + 10 pies!\"") t.equal(blurInterpolation("@{variable}"), " variable ") t.end() diff --git a/lib/utils/__tests__/containsString-test.js b/lib/utils/__tests__/containsString-test.js new file mode 100644 index 0000000000..d096a3bb8e --- /dev/null +++ b/lib/utils/__tests__/containsString-test.js @@ -0,0 +1,30 @@ +"use strict" + +const containsString = require("../containsString") +const test = require("tape") + +test("containsString comparing with string comparison values", t => { + t.deepEqual(containsString("bar", "bar"), { match: "bar", pattern: "bar" }) + t.deepEqual(containsString("foo bar something", "bar"), { match: "foo bar something", pattern: "bar" }) + t.notOk(containsString("bar", "foo")) + t.deepEqual(containsString("/bar something", "/bar"), { match: "/bar something", pattern: "/bar" }) + t.deepEqual(containsString("bar something/", "something/"), { match: "bar something/", pattern: "something/" }) + t.notOk(containsString("/bar/", "/bar/")) + t.notOk(containsString("/bar/ something", "/bar/")) + t.notOk(containsString("bar", "")) + t.notOk(containsString("bar", null)) + + t.end() +}) + +test("containsString comparing with array comparison values", t => { + t.deepEqual(containsString("bar", [ "foo", "bar" ]), { match: "bar", pattern: "bar" }) + t.deepEqual(containsString("foo baz something", [ "bar", "baz" ]), { match: "foo baz something", pattern: "baz" }) + t.deepEqual(containsString("foo bar", [ "bar", "foo" ]), { match: "foo bar", pattern: "bar" }) + t.notOk(containsString("bar", [ "foo", "baz" ])) + t.notOk(containsString("/bar/", ["/bar/"])) + t.notOk(containsString("/bar/ something", [ "/bar/", "foo" ])) + t.notOk(containsString("bar", [])) + + t.end() +}) diff --git a/src/utils/__tests__/declarationValueIndex-test.js b/lib/utils/__tests__/declarationValueIndex-test.js similarity index 79% rename from src/utils/__tests__/declarationValueIndex-test.js rename to lib/utils/__tests__/declarationValueIndex-test.js index 39d9b010b6..deefdd5ae8 100644 --- a/src/utils/__tests__/declarationValueIndex-test.js +++ b/lib/utils/__tests__/declarationValueIndex-test.js @@ -1,6 +1,8 @@ -import declarationValueIndex from "../declarationValueIndex" -import postcss from "postcss" -import test from "tape" +"use strict" + +const declarationValueIndex = require("../declarationValueIndex") +const postcss = require("postcss") +const test = require("tape") test("declarationValueIndex", t => { t.plan(5) diff --git a/lib/utils/__tests__/findAnimationName-test.js b/lib/utils/__tests__/findAnimationName-test.js new file mode 100644 index 0000000000..32df3f839f --- /dev/null +++ b/lib/utils/__tests__/findAnimationName-test.js @@ -0,0 +1,71 @@ +"use strict" + +const findAnimationName = require("../findAnimationName") +const test = require("tape") + +test("findAnimationName", t => { + t.deepEqual(findAnimationName("inherit"), [{ + sourceIndex: 0, + type: "word", + value: "inherit", + }]) + t.deepEqual(findAnimationName("INHERIT"), [{ + sourceIndex: 0, + type: "word", + value: "INHERIT", + }]) + t.deepEqual(findAnimationName("3s @varialbe"), []) + t.deepEqual(findAnimationName("3s #{$variable}"), []) + t.deepEqual(findAnimationName("none"), []) + t.deepEqual(findAnimationName("slidein"), [{ + sourceIndex: 0, + type: "word", + value: "slidein", + }]) + t.deepEqual(findAnimationName("3s slidein"), [{ + sourceIndex: 3, + type: "word", + value: "slidein", + }]) + t.deepEqual(findAnimationName("none slidein"), [{ + sourceIndex: 5, + type: "word", + value: "slidein", + }]) + t.deepEqual(findAnimationName("3s linear 1s slidein"), [{ + sourceIndex: 13, + type: "word", + value: "slidein", + }]) + t.deepEqual(findAnimationName("3S LINEAR 1S SLIDEIN"), [{ + sourceIndex: 13, + type: "word", + value: "SLIDEIN", + }]) + t.deepEqual(findAnimationName("3s ease-in 1s 2 reverse both paused slidein"), [{ + sourceIndex: 36, + type: "word", + value: "slidein", + }]) + t.deepEqual(findAnimationName("go-left-right 3s infinite alternate"), [{ + sourceIndex: 0, + type: "word", + value: "go-left-right", + }]) + t.deepEqual(findAnimationName("shrink 2s ease-out, pulsate 4s 2s infinite ease-in-out"), [ { + sourceIndex: 0, + type: "word", + value: "shrink", + }, { + sourceIndex: 20, + type: "word", + value: "pulsate", + } ]) + t.deepEqual(findAnimationName("drive 4s steps(4, end) infinite"), [{ + sourceIndex: 0, + type: "word", + value: "drive", + }]) + + t.end() +}) diff --git a/src/utils/__tests__/findAtRuleContext-test.js b/lib/utils/__tests__/findAtRuleContext-test.js similarity index 83% rename from src/utils/__tests__/findAtRuleContext-test.js rename to lib/utils/__tests__/findAtRuleContext-test.js index 226fed6091..8f5f6406dc 100644 --- a/src/utils/__tests__/findAtRuleContext-test.js +++ b/lib/utils/__tests__/findAtRuleContext-test.js @@ -1,6 +1,8 @@ -import findAtRuleContext from "../findAtRuleContext" -import postcss from "postcss" -import test from "tape" +"use strict" + +const findAtRuleContext = require("../findAtRuleContext") +const postcss = require("postcss") +const test = require("tape") test("findAtRuleContext", t => { const css = ` diff --git a/lib/utils/__tests__/findFontFamily-test.js b/lib/utils/__tests__/findFontFamily-test.js new file mode 100644 index 0000000000..a87b9bff95 --- /dev/null +++ b/lib/utils/__tests__/findFontFamily-test.js @@ -0,0 +1,215 @@ +"use strict" + +const findFontFamily = require("../findFontFamily") +const test = require("tape") + +test("findFontFamily", t => { + t.deepEqual(findFontFamily("inherit"), [{ + sourceIndex: 0, + type: "word", + value: "inherit", + }]) + t.deepEqual(findFontFamily("INHERIT"), [{ + sourceIndex: 0, + type: "word", + value: "INHERIT", + }]) + t.deepEqual(findFontFamily("12pt/10pt sans-serif"), [{ + sourceIndex: 10, + type: "word", + value: "sans-serif", + }]) + t.deepEqual(findFontFamily("12pt/10pt Red/Black"), [{ + sourceIndex: 10, + type: "word", + value: "Red/Black", + }]) + t.deepEqual(findFontFamily("12pt/10pt Red/Black/Three"), [{ + sourceIndex: 10, + type: "word", + value: "Red/Black/Three", + }]) + t.deepEqual(findFontFamily("12pt/10pt Hawaii 5-0"), [{ + sourceIndex: 10, + type: "word", + value: "Hawaii 5-0", + }]) + t.deepEqual(findFontFamily("12pt/10pt Hawaii 5-0 Font"), [{ + sourceIndex: 10, + type: "word", + value: "Hawaii 5-0 Font", + }]) + t.deepEqual(findFontFamily("12pt/10pt /* serif */ Hawaii 5-0"), [{ + sourceIndex: 22, + type: "word", + value: "Hawaii 5-0", + }]) + t.deepEqual(findFontFamily("italic bold 12px/30px $font, serif"), [{ + sourceIndex: 29, + type: "word", + value: "serif", + }]) + t.deepEqual(findFontFamily("ITALIC BOLD 12PX/30PX serif"), [{ + sourceIndex: 22, + type: "word", + value: "serif", + }]) + t.deepEqual(findFontFamily("italic bold 12px/30px #{$font}, serif"), [{ + sourceIndex: 32, + type: "word", + value: "serif", + }]) + t.deepEqual(findFontFamily("italic bold 12px/30px @font, serif"), [{ + sourceIndex: 29, + type: "word", + value: "serif", + }]) + t.deepEqual(findFontFamily("italic bold 12px/30px var(--font), serif"), [{ + sourceIndex: 35, + type: "word", + value: "serif", + }]) + t.deepEqual(findFontFamily("bold italic 110% serif"), [{ + sourceIndex: 17, + type: "word", + value: "serif", + }]) + t.deepEqual(findFontFamily("normal small-caps 12px/14px fantasy"), [{ + sourceIndex: 28, + type: "word", + value: "fantasy", + }]) + t.deepEqual(findFontFamily("italic bold 12px/30px Georgia, serif"), [ { + sourceIndex: 22, + type: "word", + value: "Georgia", + }, { + sourceIndex: 31, + type: "word", + value: "serif", + } ]) + t.deepEqual(findFontFamily("italic bold 12px / 30px Georgia, serif"), [ { + sourceIndex: 29, + type: "word", + value: "Georgia", + }, { + sourceIndex: 39, + type: "word", + value: "serif", + } ]) + t.deepEqual(findFontFamily("italic bold 12px/30px Lucida Grande, Arial, sans-serif"), [ { + sourceIndex: 22, + type: "word", + value: "Lucida Grande", + }, { + sourceIndex: 37, + type: "word", + value: "Arial", + }, { + sourceIndex: 44, + type: "word", + value: "sans-serif", + } ]) + t.deepEqual(findFontFamily("italic bold 12px/ 30px Lucida Grande, Arial, sans-serif"), [ { + sourceIndex: 23, + type: "word", + value: "Lucida Grande", + }, { + sourceIndex: 38, + type: "word", + value: "Arial", + }, { + sourceIndex: 45, + type: "word", + value: "sans-serif", + } ]) + t.deepEqual(findFontFamily("italic bold 12px /30px Lucida Grande, Arial, sans-serif"), [ { + sourceIndex: 23, + type: "word", + value: "Lucida Grande", + }, { + sourceIndex: 38, + type: "word", + value: "Arial", + }, { + sourceIndex: 45, + type: "word", + value: "sans-serif", + } ]) + t.deepEqual(findFontFamily("italic 1000 12px /30px Lucida Grande, Arial, sans-serif"), [ { + sourceIndex: 23, + type: "word", + value: "Lucida Grande", + }, { + sourceIndex: 38, + type: "word", + value: "Arial", + }, { + sourceIndex: 45, + type: "word", + value: "sans-serif", + } ]) + t.deepEqual(findFontFamily("italic bold 12px/30px Lucida Grande, Arial, sans-serif"), [ { + sourceIndex: 22, + type: "word", + value: "Lucida Grande", + }, { + sourceIndex: 37, + type: "word", + value: "Arial", + }, { + sourceIndex: 44, + type: "word", + value: "sans-serif", + } ]) + t.deepEqual(findFontFamily("italic bold 12px/30px \"Red/Black\", Arial, sans-serif"), [ { + quote: "\"", + sourceIndex: 22, + type: "string", + value: "Red/Black", + }, { + sourceIndex: 35, + type: "word", + value: "Arial", + }, { + sourceIndex: 42, + type: "word", + value: "sans-serif", + } ]) + t.deepEqual(findFontFamily("italic bold 12px/30px Arial, \"Ahem!\", sans-serif"), [ { + sourceIndex: 22, + type: "word", + value: "Arial", + }, { + quote: "\"", + sourceIndex: 29, + type: "string", + value: "Ahem!", + }, { + sourceIndex: 38, + type: "word", + value: "sans-serif", + } ]) + t.deepEqual(findFontFamily("italic bold 12px/30px \"Hawaii 5-0\", Arial, sans-serif"), [ { + quote: "\"", + sourceIndex: 22, + type: "string", + value: "Hawaii 5-0", + }, { + sourceIndex: 36, + type: "word", + value: "Arial", + }, { + sourceIndex: 43, + type: "word", + value: "sans-serif", + } ]) + + t.deepEqual(findFontFamily("16px/3 Arial"), [{ + sourceIndex: 7, + type: "word", + value: "Arial", + }]) + + t.end() +}) diff --git a/lib/utils/__tests__/findListStyleType-test.js b/lib/utils/__tests__/findListStyleType-test.js new file mode 100644 index 0000000000..9872238394 --- /dev/null +++ b/lib/utils/__tests__/findListStyleType-test.js @@ -0,0 +1,97 @@ +"use strict" + +const findListStyleType = require("../findListStyleType") +const test = require("tape") + +test("findListStyleType", t => { + t.deepEqual(findListStyleType("inherit"), [{ + sourceIndex: 0, + type: "word", + value: "inherit", + }]) + t.deepEqual(findListStyleType("INHERIT"), [{ + sourceIndex: 0, + type: "word", + value: "INHERIT", + }]) + t.deepEqual(findListStyleType("none"), [{ + sourceIndex: 0, + type: "word", + value: "none", + }]) + t.deepEqual(findListStyleType("circle"), [{ + sourceIndex: 0, + type: "word", + value: "circle", + }]) + t.deepEqual(findListStyleType("inside"), []) + t.deepEqual(findListStyleType("url('kayo.jpg')"), []) + t.deepEqual(findListStyleType("url('marker.gif') inside"), []) + t.deepEqual(findListStyleType("square outside"), [{ + sourceIndex: 0, + type: "word", + value: "square", + }]) + t.deepEqual(findListStyleType("inside square"), [{ + sourceIndex: 7, + type: "word", + value: "square", + }]) + t.deepEqual(findListStyleType("square inside url('sqpurple.gif')"), [{ + sourceIndex: 0, + type: "word", + value: "square", + }]) + t.deepEqual(findListStyleType("SQUARE INSIDE URL('sqpurple.gif')"), [{ + sourceIndex: 0, + type: "word", + value: "SQUARE", + }]) + t.deepEqual(findListStyleType("url('sqpurple.gif') square inside"), [{ + sourceIndex: 20, + type: "word", + value: "square", + }]) + t.deepEqual(findListStyleType("square url('sqpurple.gif') inside"), [{ + sourceIndex: 0, + type: "word", + value: "square", + }]) + t.deepEqual(findListStyleType("custom-counter-style"), [{ + sourceIndex: 0, + type: "word", + value: "custom-counter-style", + }]) + t.deepEqual(findListStyleType("customCounterStyle"), [{ + sourceIndex: 0, + type: "word", + value: "customCounterStyle", + }]) + t.deepEqual(findListStyleType("inside custom-counter-style"), [{ + sourceIndex: 7, + type: "word", + value: "custom-counter-style", + }]) + t.deepEqual(findListStyleType("custom-counter-style outside"), [{ + sourceIndex: 0, + type: "word", + value: "custom-counter-style", + }]) + t.deepEqual(findListStyleType("url('sqpurple.gif') custom-counter-style outside"), [{ + sourceIndex: 20, + type: "word", + value: "custom-counter-style", + }]) + t.deepEqual(findListStyleType("custom-counter-style url('sqpurple.gif') outside"), [{ + sourceIndex: 0, + type: "word", + value: "custom-counter-style", + }]) + t.deepEqual(findListStyleType("url('sqpurple.gif') outside custom-counter-style"), [{ + sourceIndex: 28, + type: "word", + value: "custom-counter-style", + }]) + + t.end() +}) diff --git a/src/utils/__tests__/fixtures/one.css b/lib/utils/__tests__/fixtures/one.css similarity index 100% rename from src/utils/__tests__/fixtures/one.css rename to lib/utils/__tests__/fixtures/one.css diff --git a/src/utils/__tests__/fixtures/two.css b/lib/utils/__tests__/fixtures/two.css similarity index 100% rename from src/utils/__tests__/fixtures/two.css rename to lib/utils/__tests__/fixtures/two.css diff --git a/src/utils/__tests__/functionArgumentsSearch-test.js b/lib/utils/__tests__/functionArgumentsSearch-test.js similarity index 93% rename from src/utils/__tests__/functionArgumentsSearch-test.js rename to lib/utils/__tests__/functionArgumentsSearch-test.js index f90d1ad9ca..ee77dac685 100644 --- a/src/utils/__tests__/functionArgumentsSearch-test.js +++ b/lib/utils/__tests__/functionArgumentsSearch-test.js @@ -1,5 +1,7 @@ -import functionArgumentsSearch from "../functionArgumentsSearch" -import test from "tape" +"use strict" + +const functionArgumentsSearch = require("../functionArgumentsSearch") +const test = require("tape") test("passes function arguments to callback", t => { functionArgumentsSearch("calc(1 + 3)", "calc", (expression, expressionIndex) => { diff --git a/src/utils/__tests__/getUnitFromValueNode-test.js b/lib/utils/__tests__/getUnitFromValueNode-test.js similarity index 90% rename from src/utils/__tests__/getUnitFromValueNode-test.js rename to lib/utils/__tests__/getUnitFromValueNode-test.js index ac9f6403e0..73162c21a6 100644 --- a/src/utils/__tests__/getUnitFromValueNode-test.js +++ b/lib/utils/__tests__/getUnitFromValueNode-test.js @@ -1,6 +1,8 @@ -import getUnitFromValueNode from "../getUnitFromValueNode" -import test from "tape" -import valueParser from "postcss-value-parser" +"use strict" + +const getUnitFromValueNode = require("../getUnitFromValueNode") +const test = require("tape") +const valueParser = require("postcss-value-parser") test("getUnitFromValueNode", t => { t.equal(getUnitFromValueNode(), null) diff --git a/src/utils/__tests__/hasBlock-test.js b/lib/utils/__tests__/hasBlock-test.js similarity index 93% rename from src/utils/__tests__/hasBlock-test.js rename to lib/utils/__tests__/hasBlock-test.js index e7d828203a..ff341e6121 100644 --- a/src/utils/__tests__/hasBlock-test.js +++ b/lib/utils/__tests__/hasBlock-test.js @@ -1,6 +1,8 @@ -import hasBlock from "../hasBlock" -import postcss from "postcss" -import test from "tape" +"use strict" + +const hasBlock = require("../hasBlock") +const postcss = require("postcss") +const test = require("tape") test("hasBlock", t => { t.ok(postcssCheck("a {}"), "empty rule") diff --git a/src/utils/__tests__/hasEmptyBlock-test.js b/lib/utils/__tests__/hasEmptyBlock-test.js similarity index 93% rename from src/utils/__tests__/hasEmptyBlock-test.js rename to lib/utils/__tests__/hasEmptyBlock-test.js index 9dd6934f91..a4f9dbf80e 100644 --- a/src/utils/__tests__/hasEmptyBlock-test.js +++ b/lib/utils/__tests__/hasEmptyBlock-test.js @@ -1,6 +1,8 @@ -import hasEmptyBlock from "../hasEmptyBlock" -import postcss from "postcss" -import test from "tape" +"use strict" + +const hasEmptyBlock = require("../hasEmptyBlock") +const postcss = require("postcss") +const test = require("tape") test("hasEmptyBlock", t => { t.ok(postcssCheck("a {}"), "empty rule") diff --git a/src/utils/__tests__/hasEmptyLine-test.js b/lib/utils/__tests__/hasEmptyLine-test.js similarity index 81% rename from src/utils/__tests__/hasEmptyLine-test.js rename to lib/utils/__tests__/hasEmptyLine-test.js index cd9604c964..07c013593b 100644 --- a/src/utils/__tests__/hasEmptyLine-test.js +++ b/lib/utils/__tests__/hasEmptyLine-test.js @@ -1,5 +1,7 @@ -import hasEmptyLine from "../hasEmptyLine" -import test from "tape" +"use strict" + +const hasEmptyLine = require("../hasEmptyLine") +const test = require("tape") test("hasEmptyLine", t => { t.ok(hasEmptyLine("\n\n")) diff --git a/src/utils/__tests__/hasInterpolation-test.js b/lib/utils/__tests__/hasInterpolation-test.js similarity index 90% rename from src/utils/__tests__/hasInterpolation-test.js rename to lib/utils/__tests__/hasInterpolation-test.js index e335898eaa..b99ad15de3 100644 --- a/src/utils/__tests__/hasInterpolation-test.js +++ b/lib/utils/__tests__/hasInterpolation-test.js @@ -1,5 +1,7 @@ -import hasInterpolation from "../hasInterpolation" -import test from "tape" +"use strict" + +const hasInterpolation = require("../hasInterpolation") +const test = require("tape") test("hasInterpolation", t => { t.ok(hasInterpolation("(min-width#{$value}: 10px)"), "SCSS interpolation") diff --git a/src/utils/__tests__/isCounterIncrementCustomIdentValue-test.js b/lib/utils/__tests__/isCounterIncrementCustomIdentValue-test.js similarity index 83% rename from src/utils/__tests__/isCounterIncrementCustomIdentValue-test.js rename to lib/utils/__tests__/isCounterIncrementCustomIdentValue-test.js index 81c1c5383d..5c954d9e98 100644 --- a/src/utils/__tests__/isCounterIncrementCustomIdentValue-test.js +++ b/lib/utils/__tests__/isCounterIncrementCustomIdentValue-test.js @@ -1,5 +1,7 @@ -import isCounterIncrementCustomIdentValue from "../isCounterIncrementCustomIdentValue" -import test from "tape" +"use strict" + +const isCounterIncrementCustomIdentValue = require("../isCounterIncrementCustomIdentValue") +const test = require("tape") test("isCustomIdents", t => { t.ok(isCounterIncrementCustomIdentValue("counter")) diff --git a/src/utils/__tests__/isCustomMediaQuery-test.js b/lib/utils/__tests__/isCustomMediaQuery-test.js similarity index 79% rename from src/utils/__tests__/isCustomMediaQuery-test.js rename to lib/utils/__tests__/isCustomMediaQuery-test.js index b8e07edeb9..0ca15909c6 100644 --- a/src/utils/__tests__/isCustomMediaQuery-test.js +++ b/lib/utils/__tests__/isCustomMediaQuery-test.js @@ -1,5 +1,7 @@ -import isCustomMediaQuery from "../isCustomMediaQuery" -import test from "tape" +"use strict" + +const isCustomMediaQuery = require("../isCustomMediaQuery") +const test = require("tape") test("isCustomMediaQuery", t => { t.ok(isCustomMediaQuery("--custom-media-query")) diff --git a/src/utils/__tests__/isCustomProperty-test.js b/lib/utils/__tests__/isCustomProperty-test.js similarity index 79% rename from src/utils/__tests__/isCustomProperty-test.js rename to lib/utils/__tests__/isCustomProperty-test.js index 52e52aab6b..ceb320c25a 100644 --- a/src/utils/__tests__/isCustomProperty-test.js +++ b/lib/utils/__tests__/isCustomProperty-test.js @@ -1,5 +1,7 @@ -import isCustomProperty from "../isCustomProperty" -import test from "tape" +"use strict" + +const isCustomProperty = require("../isCustomProperty") +const test = require("tape") test("isCustomProperty", t => { t.ok(isCustomProperty("--custom-property")) diff --git a/src/utils/__tests__/isCustomPropertySet-test.js b/lib/utils/__tests__/isCustomPropertySet-test.js similarity index 66% rename from src/utils/__tests__/isCustomPropertySet-test.js rename to lib/utils/__tests__/isCustomPropertySet-test.js index 846607e20d..d777f2fb64 100644 --- a/src/utils/__tests__/isCustomPropertySet-test.js +++ b/lib/utils/__tests__/isCustomPropertySet-test.js @@ -1,6 +1,8 @@ -import isCustomPropertySet from "../isCustomPropertySet" -import postcss from "postcss" -import test from "tape" +"use strict" + +const isCustomPropertySet = require("../isCustomPropertySet") +const postcss = require("postcss") +const test = require("tape") test("customPropertySet", t => { t.plan(2) @@ -17,8 +19,7 @@ test("customPropertySet", t => { function customPropertySet(css, cb) { postcss().process(css).then(result => { result.root.walk(cb) + }).catch(error => { + console.log(error) // eslint-disable-line no-console }) - .catch(error => { - console.log(error) // eslint-disable-line no-console - }) } diff --git a/src/utils/__tests__/isKeyframeRule-test.js b/lib/utils/__tests__/isKeyframeRule-test.js similarity index 89% rename from src/utils/__tests__/isKeyframeRule-test.js rename to lib/utils/__tests__/isKeyframeRule-test.js index c0ea1a8db3..e85bf8846f 100644 --- a/src/utils/__tests__/isKeyframeRule-test.js +++ b/lib/utils/__tests__/isKeyframeRule-test.js @@ -1,6 +1,8 @@ -import isKeyframeRule from "../isKeyframeRule" -import postcss from "postcss" -import test from "tape" +"use strict" + +const isKeyframeRule = require("../isKeyframeRule") +const postcss = require("postcss") +const test = require("tape") test("isKeyframeRule", t => { t.plan(11) diff --git a/src/utils/__tests__/isKeyframeSelector-test.js b/lib/utils/__tests__/isKeyframeSelector-test.js similarity index 87% rename from src/utils/__tests__/isKeyframeSelector-test.js rename to lib/utils/__tests__/isKeyframeSelector-test.js index d7cf8ab44e..a89456442c 100644 --- a/src/utils/__tests__/isKeyframeSelector-test.js +++ b/lib/utils/__tests__/isKeyframeSelector-test.js @@ -1,5 +1,7 @@ -import isKeyframeSelector from "../isKeyframeSelector" -import test from "tape" +"use strict" + +const isKeyframeSelector = require("../isKeyframeSelector") +const test = require("tape") test("isKeyframeSelector", t => { t.ok(isKeyframeSelector("to"), "to keyword") diff --git a/src/utils/__tests__/isNumbery-test.js b/lib/utils/__tests__/isNumbery-test.js similarity index 71% rename from src/utils/__tests__/isNumbery-test.js rename to lib/utils/__tests__/isNumbery-test.js index 19e616881c..f6db3e09d3 100644 --- a/src/utils/__tests__/isNumbery-test.js +++ b/lib/utils/__tests__/isNumbery-test.js @@ -1,5 +1,7 @@ -import isNumbery from "../isNumbery" -import test from "tape" +"use strict" + +const isNumbery = require("../isNumbery") +const test = require("tape") test("isNumbery", t => { t.ok(isNumbery("1")) diff --git a/src/utils/__tests__/isOnlyWhitespace-test.js b/lib/utils/__tests__/isOnlyWhitespace-test.js similarity index 66% rename from src/utils/__tests__/isOnlyWhitespace-test.js rename to lib/utils/__tests__/isOnlyWhitespace-test.js index 249976a82f..136329607d 100644 --- a/src/utils/__tests__/isOnlyWhitespace-test.js +++ b/lib/utils/__tests__/isOnlyWhitespace-test.js @@ -1,5 +1,7 @@ -import isOnlyWhitespace from "../isOnlyWhitespace" -import test from "tape" +"use strict" + +const isOnlyWhitespace = require("../isOnlyWhitespace") +const test = require("tape") test("isOnlyWhitespace", t => { t.ok(isOnlyWhitespace("\r\n \t \n ")) diff --git a/src/utils/__tests__/isRangeContextMediaFeature-test.js b/lib/utils/__tests__/isRangeContextMediaFeature-test.js similarity index 84% rename from src/utils/__tests__/isRangeContextMediaFeature-test.js rename to lib/utils/__tests__/isRangeContextMediaFeature-test.js index b659b38913..0c53882ec5 100644 --- a/src/utils/__tests__/isRangeContextMediaFeature-test.js +++ b/lib/utils/__tests__/isRangeContextMediaFeature-test.js @@ -1,5 +1,7 @@ -import isRangeContextMediaFeature from "../isRangeContextMediaFeature" -import test from "tape" +"use strict" + +const isRangeContextMediaFeature = require("../isRangeContextMediaFeature") +const test = require("tape") test("isRangeContextMediaFeature", t => { t.ok(isRangeContextMediaFeature("(width = 10px)")) diff --git a/src/utils/__tests__/isSingleLineString-test.js b/lib/utils/__tests__/isSingleLineString-test.js similarity index 65% rename from src/utils/__tests__/isSingleLineString-test.js rename to lib/utils/__tests__/isSingleLineString-test.js index a155f353e1..350fb04514 100644 --- a/src/utils/__tests__/isSingleLineString-test.js +++ b/lib/utils/__tests__/isSingleLineString-test.js @@ -1,10 +1,10 @@ -import isSingleLineString from "../isSingleLineString" -import test from "tape" +"use strict" -const multiLineTemplate = ( -`foo +const isSingleLineString = require("../isSingleLineString") +const test = require("tape") + +const multiLineTemplate = `foo bar` -) test("isSingleLineString", t => { t.ok(isSingleLineString("foo")) diff --git a/src/utils/__tests__/isStandardSyntaxAtRule-test.js b/lib/utils/__tests__/isStandardSyntaxAtRule-test.js similarity index 88% rename from src/utils/__tests__/isStandardSyntaxAtRule-test.js rename to lib/utils/__tests__/isStandardSyntaxAtRule-test.js index 5957874857..0d344d9181 100644 --- a/src/utils/__tests__/isStandardSyntaxAtRule-test.js +++ b/lib/utils/__tests__/isStandardSyntaxAtRule-test.js @@ -1,8 +1,10 @@ -import isStandardSyntaxAtRule from "../isStandardSyntaxAtRule" -import less from "postcss-less" -import postcss from "postcss" -import scss from "postcss-scss" -import test from "tape" +"use strict" + +const isStandardSyntaxAtRule = require("../isStandardSyntaxAtRule") +const less = require("postcss-less") +const postcss = require("postcss") +const scss = require("postcss-scss") +const test = require("tape") test("isStandardSyntaxAtRule", t => { t.plan(12) @@ -48,7 +50,9 @@ test("isStandardSyntaxAtRule", t => { }) scssAtRules("@mixin mixin() { @content; };", atRule => { - if (atRule.name === "mixin") { return } + if (atRule.name === "mixin") { + return + } t.notOk(isStandardSyntaxAtRule(atRule), "ignore `@content` inside mixins") }) diff --git a/src/utils/__tests__/isStandardSyntaxDeclaration-test.js b/lib/utils/__tests__/isStandardSyntaxDeclaration-test.js similarity index 93% rename from src/utils/__tests__/isStandardSyntaxDeclaration-test.js rename to lib/utils/__tests__/isStandardSyntaxDeclaration-test.js index cf04c2d113..ef55bc28d9 100644 --- a/src/utils/__tests__/isStandardSyntaxDeclaration-test.js +++ b/lib/utils/__tests__/isStandardSyntaxDeclaration-test.js @@ -1,8 +1,10 @@ -import isStandardSyntaxDeclaration from "../isStandardSyntaxDeclaration" -import less from "postcss-less" -import postcss from "postcss" -import scss from "postcss-scss" -import test from "tape" +"use strict" + +const isStandardSyntaxDeclaration = require("../isStandardSyntaxDeclaration") +const less = require("postcss-less") +const postcss = require("postcss") +const scss = require("postcss-scss") +const test = require("tape") test("isStandardSyntaxDeclaration", t => { t.plan(22) diff --git a/src/utils/__tests__/isStandardSyntaxFunction-test.js b/lib/utils/__tests__/isStandardSyntaxFunction-test.js similarity index 70% rename from src/utils/__tests__/isStandardSyntaxFunction-test.js rename to lib/utils/__tests__/isStandardSyntaxFunction-test.js index 6d42d25e7c..fe449657e8 100644 --- a/src/utils/__tests__/isStandardSyntaxFunction-test.js +++ b/lib/utils/__tests__/isStandardSyntaxFunction-test.js @@ -1,7 +1,9 @@ -import isStandardSyntaxFunction from "../isStandardSyntaxFunction" -import postcss from "postcss" -import test from "tape" -import valueParser from "postcss-value-parser" +"use strict" + +const isStandardSyntaxFunction = require("../isStandardSyntaxFunction") +const postcss = require("postcss") +const test = require("tape") +const valueParser = require("postcss-value-parser") test("isStandardSyntaxFunction", t => { t.plan(4) @@ -27,7 +29,9 @@ function rules(css, cb) { postcss().process(css).then(result => { result.root.walkDecls(decl => { valueParser(decl.value).walk(valueNode => { - if (valueNode.type !== "function") { return } + if (valueNode.type !== "function") { + return + } cb(valueNode) }) }) diff --git a/src/utils/__tests__/isStandardSyntaxMediaFeature-test.js b/lib/utils/__tests__/isStandardSyntaxMediaFeature-test.js similarity index 85% rename from src/utils/__tests__/isStandardSyntaxMediaFeature-test.js rename to lib/utils/__tests__/isStandardSyntaxMediaFeature-test.js index 731e416d09..31784d0493 100644 --- a/src/utils/__tests__/isStandardSyntaxMediaFeature-test.js +++ b/lib/utils/__tests__/isStandardSyntaxMediaFeature-test.js @@ -1,5 +1,7 @@ -import isStandardSyntaxMediaFeature from "../isStandardSyntaxMediaFeature" -import test from "tape" +"use strict" + +const isStandardSyntaxMediaFeature = require("../isStandardSyntaxMediaFeature") +const test = require("tape") test("isStandardSyntaxMediaFeature", t => { t.ok(isStandardSyntaxMediaFeature("(min-width: 10px)"), "prefix on range features") diff --git a/src/utils/__tests__/isStandardSyntaxMediaFeatureName-test.js b/lib/utils/__tests__/isStandardSyntaxMediaFeatureName-test.js similarity index 91% rename from src/utils/__tests__/isStandardSyntaxMediaFeatureName-test.js rename to lib/utils/__tests__/isStandardSyntaxMediaFeatureName-test.js index eb9a23ac36..84186a83d2 100644 --- a/src/utils/__tests__/isStandardSyntaxMediaFeatureName-test.js +++ b/lib/utils/__tests__/isStandardSyntaxMediaFeatureName-test.js @@ -1,5 +1,7 @@ -import isStandardSyntaxMediaFeatureName from "../isStandardSyntaxMediaFeatureName" -import test from "tape" +"use strict" + +const isStandardSyntaxMediaFeatureName = require("../isStandardSyntaxMediaFeatureName") +const test = require("tape") test("isStandardSyntaxMediaFeatureName", t => { t.ok(isStandardSyntaxMediaFeatureName("min-width"), "keyword") diff --git a/src/utils/__tests__/isStandardSyntaxProperty-test.js b/lib/utils/__tests__/isStandardSyntaxProperty-test.js similarity index 82% rename from src/utils/__tests__/isStandardSyntaxProperty-test.js rename to lib/utils/__tests__/isStandardSyntaxProperty-test.js index 29562fde3b..35f5902ad5 100644 --- a/src/utils/__tests__/isStandardSyntaxProperty-test.js +++ b/lib/utils/__tests__/isStandardSyntaxProperty-test.js @@ -1,5 +1,7 @@ -import isStandardSyntaxProperty from "../isStandardSyntaxProperty" -import test from "tape" +"use strict" + +const isStandardSyntaxProperty = require("../isStandardSyntaxProperty") +const test = require("tape") test("isStandardSyntaxProperty", t => { t.ok(isStandardSyntaxProperty("top"), "single word") diff --git a/src/utils/__tests__/isStandardSyntaxRule-test.js b/lib/utils/__tests__/isStandardSyntaxRule-test.js similarity index 93% rename from src/utils/__tests__/isStandardSyntaxRule-test.js rename to lib/utils/__tests__/isStandardSyntaxRule-test.js index 4ea8fdefa7..e6c89bf029 100644 --- a/src/utils/__tests__/isStandardSyntaxRule-test.js +++ b/lib/utils/__tests__/isStandardSyntaxRule-test.js @@ -1,7 +1,9 @@ -import isStandardSyntaxRule from "../isStandardSyntaxRule" -import less from "postcss-less" -import postcss from "postcss" -import test from "tape" +"use strict" + +const isStandardSyntaxRule = require("../isStandardSyntaxRule") +const less = require("postcss-less") +const postcss = require("postcss") +const test = require("tape") test("isStandardSyntaxRule", t => { t.plan(20) diff --git a/src/utils/__tests__/isStandardSyntaxSelector-test.js b/lib/utils/__tests__/isStandardSyntaxSelector-test.js similarity index 89% rename from src/utils/__tests__/isStandardSyntaxSelector-test.js rename to lib/utils/__tests__/isStandardSyntaxSelector-test.js index ebd7a0970f..393fc12824 100644 --- a/src/utils/__tests__/isStandardSyntaxSelector-test.js +++ b/lib/utils/__tests__/isStandardSyntaxSelector-test.js @@ -1,5 +1,7 @@ -import isStandardSyntaxSelector from "../isStandardSyntaxSelector" -import test from "tape" +"use strict" + +const isStandardSyntaxSelector = require("../isStandardSyntaxSelector") +const test = require("tape") test("isStandardSyntaxSelector", t => { t.ok(isStandardSyntaxSelector("a"), "type") diff --git a/src/utils/__tests__/isStandardSyntaxTypeSelector-test.js b/lib/utils/__tests__/isStandardSyntaxTypeSelector-test.js similarity index 85% rename from src/utils/__tests__/isStandardSyntaxTypeSelector-test.js rename to lib/utils/__tests__/isStandardSyntaxTypeSelector-test.js index 03e18302f3..1dcf77ea79 100644 --- a/src/utils/__tests__/isStandardSyntaxTypeSelector-test.js +++ b/lib/utils/__tests__/isStandardSyntaxTypeSelector-test.js @@ -1,7 +1,9 @@ -import isStandardSyntaxTypeSelector from "../isStandardSyntaxTypeSelector" -import postcss from "postcss" -import selectorParser from "postcss-selector-parser" -import test from "tape" +"use strict" + +const isStandardSyntaxTypeSelector = require("../isStandardSyntaxTypeSelector") +const postcss = require("postcss") +const selectorParser = require("postcss-selector-parser") +const test = require("tape") test("isStandardSyntaxTypeSelector", t => { t.plan(8) diff --git a/src/utils/__tests__/isStandardSyntaxUrl-test.js b/lib/utils/__tests__/isStandardSyntaxUrl-test.js similarity index 77% rename from src/utils/__tests__/isStandardSyntaxUrl-test.js rename to lib/utils/__tests__/isStandardSyntaxUrl-test.js index 678d5d5212..da7cdf593c 100644 --- a/src/utils/__tests__/isStandardSyntaxUrl-test.js +++ b/lib/utils/__tests__/isStandardSyntaxUrl-test.js @@ -1,5 +1,7 @@ -import isStandardSyntaxUrl from "../isStandardSyntaxUrl" -import test from "tape" +"use strict" + +const isStandardSyntaxUrl = require("../isStandardSyntaxUrl") +const test = require("tape") test("isStandardSyntaxUrl", t => { t.ok(isStandardSyntaxUrl(""), "empty") @@ -12,38 +14,15 @@ test("isStandardSyntaxUrl", t => { t.ok(isStandardSyntaxUrl("./some/path/to/file.png"), "url with single-dot path segment") t.ok(isStandardSyntaxUrl("../some/path/to/file.png"), "url with double-dot path segment") t.ok(isStandardSyntaxUrl("https://www.domain.com:8080/file.jpg"), "url with port") - t.ok( - isStandardSyntaxUrl( - "data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7" - ), - "url with local scheme" - ) + t.ok(isStandardSyntaxUrl("data:image/gif;base64,R0lGODlhEAAQAMQAAORHHOVSKudfOulrSOp3WOyDZu6QdvCchPGolfO0o/XBs/fNwfjZ0frl3/zy7////wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAkAABAALAAAAAAQABAAAAVVICSOZGlCQAosJ6mu7fiyZeKqNKToQGDsM8hBADgUXoGAiqhSvp5QAnQKGIgUhwFUYLCVDFCrKUE1lBavAViFIDlTImbKC5Gm2hB0SlBCBMQiB0UjIQA7"), "url with local scheme") - t.ok( - isStandardSyntaxUrl("http://domain.com:8080/path/to$to/file$2x.ext?$1=$2"), - "URL with protocol(http) and `$` character without quotes" - ) - t.ok( - isStandardSyntaxUrl("'http://domain.com:8080/path$path/to$to/$file$2x.ext?$1$2=$1$2'"), - "URL with protocol(http) and `$` character in single quotes" - ) - t.ok( - isStandardSyntaxUrl("\"http://domain.com:8080/path$path/to$to/$file$2x.ext?$1$2=$1$2\""), - "URL with protocol(http) and `$` character in double quotes" - ) + t.ok(isStandardSyntaxUrl("http://domain.com:8080/path/to$to/file$2x.ext?$1=$2"), "URL with protocol(http) and `$` character without quotes") + t.ok(isStandardSyntaxUrl("'http://domain.com:8080/path$path/to$to/$file$2x.ext?$1$2=$1$2'"), "URL with protocol(http) and `$` character in single quotes") + t.ok(isStandardSyntaxUrl("\"http://domain.com:8080/path$path/to$to/$file$2x.ext?$1$2=$1$2\""), "URL with protocol(http) and `$` character in double quotes") - t.ok( - isStandardSyntaxUrl("http://domain.com:8080/path/to@to/file@2x.ext?@1=@2"), - "URL with protocol(http) and `@` character without quotes" - ) - t.ok( - isStandardSyntaxUrl("'http://domain.com:8080/path$path/to@to/$file@2x.ext?@1$2=$1@2'"), - "URL with protocol(http) and `@` character in single quotes" - ) - t.ok( - isStandardSyntaxUrl("\"http://domain.com:8080/path$path/to@to/$file@2x.ext?@1$2=$1@2\""), - "URL with protocol(http) and `@` character in double quotes" - ) + t.ok(isStandardSyntaxUrl("http://domain.com:8080/path/to@to/file@2x.ext?@1=@2"), "URL with protocol(http) and `@` character without quotes") + t.ok(isStandardSyntaxUrl("'http://domain.com:8080/path$path/to@to/$file@2x.ext?@1$2=$1@2'"), "URL with protocol(http) and `@` character in single quotes") + t.ok(isStandardSyntaxUrl("\"http://domain.com:8080/path$path/to@to/$file@2x.ext?@1$2=$1@2\""), "URL with protocol(http) and `@` character in double quotes") t.ok(isStandardSyntaxUrl("\"$url/path\""), "url with dollar at the start and double quotes") t.ok(isStandardSyntaxUrl("\"some/$url/path\""), "url with dollar in the middle and double quotes") diff --git a/src/utils/__tests__/isStandardSyntaxValue-test.js b/lib/utils/__tests__/isStandardSyntaxValue-test.js similarity index 82% rename from src/utils/__tests__/isStandardSyntaxValue-test.js rename to lib/utils/__tests__/isStandardSyntaxValue-test.js index 16162bdb19..94b17c7fae 100644 --- a/src/utils/__tests__/isStandardSyntaxValue-test.js +++ b/lib/utils/__tests__/isStandardSyntaxValue-test.js @@ -1,5 +1,7 @@ -import isStandardSyntaxValue from "../isStandardSyntaxValue" -import test from "tape" +"use strict" + +const isStandardSyntaxValue = require("../isStandardSyntaxValue") +const test = require("tape") test("isStandardSyntaxValue", t => { t.ok(isStandardSyntaxValue("initial"), "keyword") diff --git a/src/utils/__tests__/isValidFontSize-test.js b/lib/utils/__tests__/isValidFontSize-test.js similarity index 74% rename from src/utils/__tests__/isValidFontSize-test.js rename to lib/utils/__tests__/isValidFontSize-test.js index e549612d68..d91385e40e 100644 --- a/src/utils/__tests__/isValidFontSize-test.js +++ b/lib/utils/__tests__/isValidFontSize-test.js @@ -1,5 +1,7 @@ -import isValidFontSize from "../isValidFontSize" -import test from "tape" +"use strict" + +const isValidFontSize = require("../isValidFontSize") +const test = require("tape") test("isValidFontSize", t => { t.ok(isValidFontSize("10px")) diff --git a/src/utils/__tests__/isValidHex-test.js b/lib/utils/__tests__/isValidHex-test.js similarity index 84% rename from src/utils/__tests__/isValidHex-test.js rename to lib/utils/__tests__/isValidHex-test.js index f3124358ae..9048f3e938 100644 --- a/src/utils/__tests__/isValidHex-test.js +++ b/lib/utils/__tests__/isValidHex-test.js @@ -1,5 +1,7 @@ -import isValidHex from "../isValidHex" -import test from "tape" +"use strict" + +const isValidHex = require("../isValidHex") +const test = require("tape") test("isValidHex", t => { t.ok(isValidHex("#333")) diff --git a/src/utils/__tests__/isVariable-test.js b/lib/utils/__tests__/isVariable-test.js similarity index 83% rename from src/utils/__tests__/isVariable-test.js rename to lib/utils/__tests__/isVariable-test.js index 8b6d441401..8c064a25e9 100644 --- a/src/utils/__tests__/isVariable-test.js +++ b/lib/utils/__tests__/isVariable-test.js @@ -1,5 +1,7 @@ -import isVariable from "../isVariable" -import test from "tape" +"use strict" + +const isVariable = require("../isVariable") +const test = require("tape") test("isVariable", t => { t.ok(isVariable("var(--something)")) diff --git a/src/utils/__tests__/matchesStringOrRegExp-test.js b/lib/utils/__tests__/matchesStringOrRegExp-test.js similarity index 56% rename from src/utils/__tests__/matchesStringOrRegExp-test.js rename to lib/utils/__tests__/matchesStringOrRegExp-test.js index b7464708ea..deebf44c6e 100644 --- a/src/utils/__tests__/matchesStringOrRegExp-test.js +++ b/lib/utils/__tests__/matchesStringOrRegExp-test.js @@ -1,53 +1,42 @@ -import matchesStringOrRegExp from "../matchesStringOrRegExp" -import test from "tape" +"use strict" + +const matchesStringOrRegExp = require("../matchesStringOrRegExp") +const test = require("tape") test("matchesStringOrRegExp comparing with string comparisonValues", t => { - t.deepEqual(matchesStringOrRegExp("bar", "bar"), - { match: "bar", pattern: "bar" }) + t.deepEqual(matchesStringOrRegExp("bar", "bar"), { match: "bar", pattern: "bar" }) t.notOk(matchesStringOrRegExp("bar", "/bar something")) - t.deepEqual(matchesStringOrRegExp("/bar something", "/bar something"), - { match: "/bar something", pattern: "/bar something" }) - t.deepEqual(matchesStringOrRegExp("bar something/", "bar something/"), - { match: "bar something/", pattern: "bar something/" }) + t.deepEqual(matchesStringOrRegExp("/bar something", "/bar something"), { match: "/bar something", pattern: "/bar something" }) + t.deepEqual(matchesStringOrRegExp("bar something/", "bar something/"), { match: "bar something/", pattern: "bar something/" }) t.notOk(matchesStringOrRegExp("bar something/", "bar something//")) - t.deepEqual(matchesStringOrRegExp([ "foo", "bar" ], "bar"), - { match: "bar", pattern: "bar" }) + t.deepEqual(matchesStringOrRegExp([ "foo", "bar" ], "bar"), { match: "bar", pattern: "bar" }) t.notOk(matchesStringOrRegExp([ "foo", "baz" ], "bar")) - t.deepEqual(matchesStringOrRegExp("bar", [ "foo", "bar" ]), - { match: "bar", pattern: "bar" }) + t.deepEqual(matchesStringOrRegExp("bar", [ "foo", "bar" ]), { match: "bar", pattern: "bar" }) t.notOk(matchesStringOrRegExp("bar", [ "foo", "baz" ])) - t.deepEqual(matchesStringOrRegExp([ "foo", "baz" ], [ "foo", "bar" ]), - { match: "foo", pattern: "foo" }) + t.deepEqual(matchesStringOrRegExp([ "foo", "baz" ], [ "foo", "bar" ]), { match: "foo", pattern: "foo" }) t.notOk(matchesStringOrRegExp([ "bar", "hooha" ], [ "foo", "baz" ])) t.end() }) test("matchesStringOrRegExp comparing with a RegExp comparisonValue", t => { - t.deepEqual(matchesStringOrRegExp(".foo", "/\\.foo$/"), - { match: ".foo", pattern: "/\\.foo$/" }) - t.deepEqual(matchesStringOrRegExp("bar .foo", "/\\.foo$/"), - { match: "bar .foo", pattern: "/\\.foo$/" }) + t.deepEqual(matchesStringOrRegExp(".foo", "/\\.foo$/"), { match: ".foo", pattern: "/\\.foo$/" }) + t.deepEqual(matchesStringOrRegExp("bar .foo", "/\\.foo$/"), { match: "bar .foo", pattern: "/\\.foo$/" }) t.notOk(matchesStringOrRegExp("bar .foo bar", "/\\.foo$/")) t.notOk(matchesStringOrRegExp("foo", "/\\.foo$/")) - t.deepEqual(matchesStringOrRegExp([ ".foo", "bar" ], "/\\.foo$/"), - { match: ".foo", pattern: "/\\.foo$/" }) + t.deepEqual(matchesStringOrRegExp([ ".foo", "bar" ], "/\\.foo$/"), { match: ".foo", pattern: "/\\.foo$/" }) t.notOk(matchesStringOrRegExp([ "foo", "baz" ], "/\\.foo$/")) - t.deepEqual(matchesStringOrRegExp(".foo", [ "/\\.foo$/", "/^bar/" ]), - { match: ".foo", pattern: "/\\.foo$/" }) - t.deepEqual(matchesStringOrRegExp("bar", [ "/\\.foo$/", "/^bar/" ]), - { match: "bar", pattern: "/^bar/" }) + t.deepEqual(matchesStringOrRegExp(".foo", [ "/\\.foo$/", "/^bar/" ]), { match: ".foo", pattern: "/\\.foo$/" }) + t.deepEqual(matchesStringOrRegExp("bar", [ "/\\.foo$/", "/^bar/" ]), { match: "bar", pattern: "/^bar/" }) t.notOk(matchesStringOrRegExp("ebarz", [ "/\\.foo$/", "/^bar/" ])) - t.deepEqual(matchesStringOrRegExp([ ".foo", "ebarz" ], [ "/\\.foo$/", "/^bar/" ]), - { match: ".foo", pattern: "/\\.foo$/" }) - t.deepEqual(matchesStringOrRegExp([ "bar", "foo" ], [ "/\\.foo$/", "/^bar/" ]), - { match: "bar", pattern: "/^bar/" }) + t.deepEqual(matchesStringOrRegExp([ ".foo", "ebarz" ], [ "/\\.foo$/", "/^bar/" ]), { match: ".foo", pattern: "/\\.foo$/" }) + t.deepEqual(matchesStringOrRegExp([ "bar", "foo" ], [ "/\\.foo$/", "/^bar/" ]), { match: "bar", pattern: "/^bar/" }) t.notOk(matchesStringOrRegExp([ "ebarz", "foo" ], [ "/\\.foo$/", "/^bar/" ])) t.end() diff --git a/src/utils/__tests__/nextNonCommentNode-test.js b/lib/utils/__tests__/nextNonCommentNode-test.js similarity index 87% rename from src/utils/__tests__/nextNonCommentNode-test.js rename to lib/utils/__tests__/nextNonCommentNode-test.js index edaa555bb2..d47cad1347 100644 --- a/src/utils/__tests__/nextNonCommentNode-test.js +++ b/lib/utils/__tests__/nextNonCommentNode-test.js @@ -1,6 +1,8 @@ -import nextNonCommentNode from "../nextNonCommentNode" -import postcss from "postcss" -import test from "tape" +"use strict" + +const nextNonCommentNode = require("../nextNonCommentNode") +const postcss = require("postcss") +const test = require("tape") test("nextNonCommentNode", t => { let planned = 0 diff --git a/lib/utils/__tests__/nodeContextLookup-test.js b/lib/utils/__tests__/nodeContextLookup-test.js new file mode 100644 index 0000000000..2f80090e64 --- /dev/null +++ b/lib/utils/__tests__/nodeContextLookup-test.js @@ -0,0 +1,31 @@ +"use strict" + +const nodeContextLookup = require("../nodeContextLookup") +const path = require("path") +const postcss = require("postcss") +const postcssImport = require("postcss-import") +const test = require("tape") + +test("nodeContextLookup checking media context", t => { + const testLookup = nodeContextLookup() + + t.plan(8) + postcss([postcssImport()]).process("@import 'fixtures/one.css'; @import 'fixtures/two.css';", { + from: path.join(__dirname, "fake.css"), + }).then(result => { + const rulesBySelector = {} + result.root.walkRules(rule => { + rulesBySelector[rule.selector] = rule + }) + + // a-d are in one file; e-h in another + t.equal(testLookup.getContext(rulesBySelector.a, rulesBySelector.a.parent), testLookup.getContext(rulesBySelector.b, rulesBySelector.b.parent)) + t.notEqual(testLookup.getContext(rulesBySelector.a, rulesBySelector.a.parent), testLookup.getContext(rulesBySelector.c, rulesBySelector.c.parent)) + t.notEqual(testLookup.getContext(rulesBySelector.a, rulesBySelector.a.parent), testLookup.getContext(rulesBySelector.e, rulesBySelector.e.parent)) + t.equal(testLookup.getContext(rulesBySelector.c, rulesBySelector.c.parent), testLookup.getContext(rulesBySelector.d, rulesBySelector.d.parent)) + t.notEqual(testLookup.getContext(rulesBySelector.c, rulesBySelector.c.parent), testLookup.getContext(rulesBySelector.g, rulesBySelector.g.parent)) + t.equal(testLookup.getContext(rulesBySelector.e, rulesBySelector.e.parent), testLookup.getContext(rulesBySelector.f, rulesBySelector.f.parent)) + t.notEqual(testLookup.getContext(rulesBySelector.f, rulesBySelector.f.parent), testLookup.getContext(rulesBySelector.g, rulesBySelector.g.parent)) + t.equal(testLookup.getContext(rulesBySelector.g, rulesBySelector.g.parent), testLookup.getContext(rulesBySelector.h, rulesBySelector.h.parent)) + }).catch(err => console.log(err.stack)); // eslint-disable-line +}) diff --git a/src/utils/__tests__/optionsMatches-test.js b/lib/utils/__tests__/optionsMatches-test.js similarity index 92% rename from src/utils/__tests__/optionsMatches-test.js rename to lib/utils/__tests__/optionsMatches-test.js index bedb0f6596..ea3f3badf1 100644 --- a/src/utils/__tests__/optionsMatches-test.js +++ b/lib/utils/__tests__/optionsMatches-test.js @@ -1,5 +1,7 @@ -import optionsMatches from "../optionsMatches" -import test from "tape" +"use strict" + +const optionsMatches = require("../optionsMatches") +const test = require("tape") test("optionsMatches matches a string", t => { t.ok(optionsMatches({ foo: "bar" }, "foo", "bar")) diff --git a/src/utils/__tests__/report-test.js b/lib/utils/__tests__/report-test.js similarity index 91% rename from src/utils/__tests__/report-test.js rename to lib/utils/__tests__/report-test.js index 017c9a7485..05a9e88d4e 100644 --- a/src/utils/__tests__/report-test.js +++ b/lib/utils/__tests__/report-test.js @@ -1,6 +1,8 @@ -import report from "../report" -import sinon from "sinon" -import test from "tape" +"use strict" + +const report = require("../report") +const sinon = require("sinon") +const test = require("tape") test("without disabledRanges", t => { const v = { @@ -117,10 +119,7 @@ test("with relevant general disabledRange, among others", t => { warn: sinon.spy(), stylelint: { disabledRanges: { - all: [ - { start: 1, end: 3 }, - { start: 5, end: 8 }, - ], + all: [ { start: 1, end: 3 }, { start: 5, end: 8 } ], }, }, }, @@ -142,10 +141,7 @@ test("with relevant rule-specific disabledRange, among others", t => { stylelint: { disabledRanges: { all: [], - foo: [ - { start: 1, end: 3, rules: ["foo"] }, - { start: 5, end: 8, rules: ["foo"] }, - ], + foo: [ { start: 1, end: 3, rules: ["foo"] }, { start: 5, end: 8, rules: ["foo"] } ], }, }, }, diff --git a/src/utils/__tests__/ruleMessages-test.js b/lib/utils/__tests__/ruleMessages-test.js similarity index 68% rename from src/utils/__tests__/ruleMessages-test.js rename to lib/utils/__tests__/ruleMessages-test.js index 03dc1b67f7..70a4c3df5d 100644 --- a/src/utils/__tests__/ruleMessages-test.js +++ b/lib/utils/__tests__/ruleMessages-test.js @@ -1,16 +1,16 @@ -import ruleMessages from "../ruleMessages" -import test from "tape" +"use strict" + +const ruleMessages = require("../ruleMessages") +const test = require("tape") test("ruleMessages with simple messages", t => { - t.deepEqual( - ruleMessages("foo", { - good: "GOOD", - bad: "BAD", - }), - { - good: "GOOD (foo)", - bad: "BAD (foo)", - }) + t.deepEqual(ruleMessages("foo", { + good: "GOOD", + bad: "BAD", + }), { + good: "GOOD (foo)", + bad: "BAD (foo)", + }) t.end() }) diff --git a/src/utils/__tests__/validateObjectWithStringArrayProps-test.js b/lib/utils/__tests__/validateObjectWithStringArrayProps-test.js similarity index 81% rename from src/utils/__tests__/validateObjectWithStringArrayProps-test.js rename to lib/utils/__tests__/validateObjectWithStringArrayProps-test.js index 30693fb7ed..af111b383a 100644 --- a/src/utils/__tests__/validateObjectWithStringArrayProps-test.js +++ b/lib/utils/__tests__/validateObjectWithStringArrayProps-test.js @@ -1,5 +1,7 @@ -import test from "tape" -import validateObjectWithStringArrayProps from "../validateObjectWithStringArrayProps" +"use strict" + +const test = require("tape") +const validateObjectWithStringArrayProps = require("../validateObjectWithStringArrayProps") test("validateObjectWithStringArrayProps", t => { t.ok(validateObjectWithStringArrayProps({ prop: ["val"] })) @@ -13,15 +15,15 @@ test("validateObjectWithStringArrayProps", t => { t.notOk(validateObjectWithStringArrayProps({ prop: 1 })) t.notOk(validateObjectWithStringArrayProps({ prop: "string" })) t.notOk(validateObjectWithStringArrayProps({ prop: null })) - t.notOk(validateObjectWithStringArrayProps({ prop: { } })) + t.notOk(validateObjectWithStringArrayProps({ prop: {} })) t.notOk(validateObjectWithStringArrayProps({ prop: [ "1", 1 ] })) t.notOk(validateObjectWithStringArrayProps({ prop: [ "null", null ] })) - t.notOk(validateObjectWithStringArrayProps({ prop: [ "object", { } ] })) + t.notOk(validateObjectWithStringArrayProps({ prop: [ "object", {} ] })) t.notOk(validateObjectWithStringArrayProps({ prop1: ["1"], prop2: [ "1", 1 ] })) t.notOk(validateObjectWithStringArrayProps({ prop1: ["1"], prop2: [ "null", null ] })) - t.notOk(validateObjectWithStringArrayProps({ prop1: ["1"], prop2: [ "object", { } ] })) + t.notOk(validateObjectWithStringArrayProps({ prop1: ["1"], prop2: [ "object", {} ] })) t.end() }) diff --git a/src/utils/__tests__/validateOptions-test.js b/lib/utils/__tests__/validateOptions-test.js similarity index 93% rename from src/utils/__tests__/validateOptions-test.js rename to lib/utils/__tests__/validateOptions-test.js index cf78e0f9ba..f1f5e0a75a 100644 --- a/src/utils/__tests__/validateOptions-test.js +++ b/lib/utils/__tests__/validateOptions-test.js @@ -1,6 +1,8 @@ -import sinon from "sinon" -import test from "tape" -import validateOptions from "../validateOptions" +"use strict" + +const sinon = require("sinon") +const test = require("tape") +const validateOptions = require("../validateOptions") function mockResult() { return { warn: sinon.spy() } @@ -213,9 +215,15 @@ test("validateOptions for multiple actual/possible pairs, checking return value" test("validateOptions with a function for 'possible'", t => { const result = mockResult() const schema = x => { - if (x === "bar") { return true } - if (!Array.isArray(x)) { return false } - if (x.every(item => typeof item === "string" || !!item.properties)) { return true } + if (x === "bar") { + return true + } + if (!Array.isArray(x)) { + return false + } + if (x.every(item => typeof item === "string" || !!item.properties)) { + return true + } return false } @@ -237,10 +245,7 @@ test("validateOptions with a function for 'possible'", t => { const validArrayOfObjects = validateOptions(result, "foo", { possible: schema, - actual: [ - { properties: ["one"] }, - { properties: [ "two", "three" ] }, - ], + actual: [ { properties: ["one"] }, { properties: [ "two", "three" ] } ], }) t.equal(validArrayOfObjects, true, "array of objects passes") t.notOk(result.warn.called) @@ -248,11 +253,7 @@ test("validateOptions with a function for 'possible'", t => { const validArrayOfObjectsAndStrings = validateOptions(result, "foo", { possible: schema, - actual: [ - { properties: ["one"] }, - { properties: [ "two", "three" ] }, - "four", - ], + actual: [ { properties: ["one"] }, { properties: [ "two", "three" ] }, "four" ], }) t.equal(validArrayOfObjectsAndStrings, true, "array of mixed objects and strings passes") t.notOk(result.warn.called) @@ -274,7 +275,7 @@ test("validateOptions for null instead of array", t => { const result = mockResult() validateOptions(result, "no-dancing", { actual: null, - possible: [(v) => typeof v === "string"], + possible: [v => typeof v === "string"], }) t.notOk(result.warn.called) t.end() @@ -284,7 +285,7 @@ test("validateOptions for arrayed null instead of array", t => { const result = mockResult() validateOptions(result, "no-dancing", { actual: [null], - possible: [(v) => typeof v === "string"], + possible: [v => typeof v === "string"], }) t.notOk(result.warn.called) t.end() diff --git a/src/utils/atRuleParamIndex.js b/lib/utils/atRuleParamIndex.js similarity index 51% rename from src/utils/atRuleParamIndex.js rename to lib/utils/atRuleParamIndex.js index f205068462..3679d94356 100644 --- a/src/utils/atRuleParamIndex.js +++ b/lib/utils/atRuleParamIndex.js @@ -1,11 +1,6 @@ /* @flow */ -/** - * Get the index of a media query's params - * - * @param {AtRule} atRule - * @return {int} The index - */ -export default function (atRule: postcss$atRule): number { +"use strict" +module.exports = function (atRule/*: postcss$atRule*/)/*: number*/ { // Initial 1 is for the `@` let index = 1 + atRule.name.length if (atRule.raws.afterName) { diff --git a/lib/utils/beforeBlockString.js b/lib/utils/beforeBlockString.js new file mode 100644 index 0000000000..2d80109563 --- /dev/null +++ b/lib/utils/beforeBlockString.js @@ -0,0 +1,38 @@ +/* @flow */ +"use strict" +module.exports = function (statement/*: Object*/, options/*:: ?: Object*/)/*: string*/ { + options = options || {} + + let result = "" + let rule/*: postcss$rule*/ + let atRule/*: postcss$atRule*/ + + if (statement.type === "rule") { + rule = statement + } + if (statement.type === "atrule") { + atRule = statement + } + + if (!rule && !atRule) { + return result + } + + const before = statement.raws.before + const between = statement.raws.between + + if (!options.noRawBefore) { + result += before + } + if (rule) { + result += rule.selector + } + if (atRule) { + result += "@" + atRule.name + atRule.raws.afterName + atRule.params + } + if (between !== undefined) { + result += between + } + + return result +} diff --git a/src/utils/blockString.js b/lib/utils/blockString.js similarity index 55% rename from src/utils/blockString.js rename to lib/utils/blockString.js index 517ddbdb4a..d093070075 100644 --- a/src/utils/blockString.js +++ b/lib/utils/blockString.js @@ -1,7 +1,8 @@ /* @flow */ -import beforeBlockString from "./beforeBlockString" -import hasBlock from "./hasBlock" -import rawNodeString from "./rawNodeString" +"use strict" +const beforeBlockString = require("./beforeBlockString") +const hasBlock = require("./hasBlock") +const rawNodeString = require("./rawNodeString") /** * Return a CSS statement's block -- the string that starts and `{` and ends with `}`. @@ -12,9 +13,9 @@ import rawNodeString from "./rawNodeString" * @param {Rule|AtRule} statement - postcss rule or at-rule node * @return {string|undefined} */ -export default function ( - statement: postcss$rule | postcss$atRule -): string | boolean { - if (!hasBlock(statement)) { return false } +module.exports = function (statement/*: postcss$rule | postcss$atRule*/)/*: string | boolean*/ { + if (!hasBlock(statement)) { + return false + } return rawNodeString(statement).slice(beforeBlockString(statement).length) } diff --git a/lib/utils/blurComments.js b/lib/utils/blurComments.js new file mode 100644 index 0000000000..c4cc6b09df --- /dev/null +++ b/lib/utils/blurComments.js @@ -0,0 +1,7 @@ +/* @flow */ +"use strict" +module.exports = function (source/*: string*/)/*: string*/ { + const blurChar/*: string*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "`" + + return source.replace(/\/\*.*\*\//g, blurChar) +} diff --git a/src/utils/blurFunctionArguments.js b/lib/utils/blurFunctionArguments.js similarity index 75% rename from src/utils/blurFunctionArguments.js rename to lib/utils/blurFunctionArguments.js index dbdac3eda0..ea06a59ba8 100644 --- a/src/utils/blurFunctionArguments.js +++ b/lib/utils/blurFunctionArguments.js @@ -1,6 +1,7 @@ /* @flow */ -import _ from "lodash" -import balancedMatch from "balanced-match" +"use strict" +const _ = require("lodash") +const balancedMatch = require("balanced-match") /** * Replace all of the characters that are arguments to a certain @@ -18,16 +19,16 @@ import balancedMatch from "balanced-match" * @param {[string]} blurChar="`" * @return {string} - The result string, with the function arguments "blurred" */ -export default function ( - source: string, - functionName: string, - blurChar: string = "`" -): string { +module.exports = function (source/*: string*/, functionName/*: string*/)/*: string*/ { + const blurChar/*: string*/ = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "`" + const nameWithParen = `${functionName.toLowerCase()}(` const lowerCaseSource = source.toLowerCase() - if (!_.includes(lowerCaseSource, nameWithParen)) { return source } + if (!_.includes(lowerCaseSource, nameWithParen)) { + return source + } - const functionNameLength: number = functionName.length + const functionNameLength/*: number*/ = functionName.length let result = source let searchStartIndex = 0 diff --git a/lib/utils/blurInterpolation.js b/lib/utils/blurInterpolation.js new file mode 100644 index 0000000000..67acfbb545 --- /dev/null +++ b/lib/utils/blurInterpolation.js @@ -0,0 +1,7 @@ +/* @flow */ +"use strict" +module.exports = function (source/*: string*/)/*: string*/ { + const blurChar/*: string*/ = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : " " + + return source.replace(/[#@{}]+/g, blurChar) +} diff --git a/src/utils/configurationError.js b/lib/utils/configurationError.js similarity index 81% rename from src/utils/configurationError.js rename to lib/utils/configurationError.js index 24a1c18c22..ff6925ae7a 100644 --- a/src/utils/configurationError.js +++ b/lib/utils/configurationError.js @@ -1,10 +1,12 @@ +"use strict" + /** * Create configurationError from text and set CLI exit code * * @param {string} text * @return {Error} - The error, with text and exit code */ -export default function (text) { +module.exports = function (text) { const err = new Error(text) err.code = 78 return err diff --git a/src/utils/containsString.js b/lib/utils/containsString.js similarity index 93% rename from src/utils/containsString.js rename to lib/utils/containsString.js index f32b54eb06..8192692e51 100644 --- a/src/utils/containsString.js +++ b/lib/utils/containsString.js @@ -1,3 +1,5 @@ +"use strict" + /** * Checks if a string contains a value. The comparison value can be a string or * an array of strings. @@ -12,7 +14,7 @@ * - `match`: the `input` value that had a match * - `pattern`: the `comparison` pattern that had a match */ -export default function containsString(input, comparison) { +module.exports = function containsString(input, comparison) { if (!Array.isArray(comparison)) { return testAgainstString(input, comparison) } diff --git a/src/utils/declarationValueIndex.js b/lib/utils/declarationValueIndex.js similarity index 84% rename from src/utils/declarationValueIndex.js rename to lib/utils/declarationValueIndex.js index ee40a228da..9b745aff69 100644 --- a/src/utils/declarationValueIndex.js +++ b/lib/utils/declarationValueIndex.js @@ -1,10 +1,12 @@ +"use strict" + /** * Get the index of a declaration's value * * @param {Decl} decl * @return {int} The index */ -export default function (decl) { +module.exports = function (decl) { const beforeColon = decl.toString().indexOf(":") const afterColon = decl.raw("between").length - decl.raw("between").indexOf(":") return beforeColon + afterColon diff --git a/lib/utils/findAnimationName.js b/lib/utils/findAnimationName.js new file mode 100644 index 0000000000..0b618dc816 --- /dev/null +++ b/lib/utils/findAnimationName.js @@ -0,0 +1,57 @@ +"use strict" + +const keywordSets = require("../reference/keywordSets") +const getUnitFromValueNode = require("./getUnitFromValueNode") +const isStandardSyntaxValue = require("./isStandardSyntaxValue") +const isVariable = require("./isVariable") +const postcssValueParser = require("postcss-value-parser") + +/** + * Get the font-families within a `font` shorthand property value. + * + * @param {string} value + * @return {object} Collection font-family nodes + */ +module.exports = function findAnimationName(value) { + const animationNames = [] + + const valueNodes = postcssValueParser(value) + + // Handle `inherit`, `initial` and etc + if (valueNodes.nodes.length === 1 && keywordSets.basicKeywords.has(valueNodes.nodes[0].value.toLowerCase())) { + return [valueNodes.nodes[0]] + } + + valueNodes.walk(valueNode => { + if (valueNode.type === "function") { + return false + } + if (valueNode.type !== "word") { + return + } + + const valueLowerCase = valueNode.value.toLowerCase() + + // Ignore non standard syntax + if (!isStandardSyntaxValue(valueLowerCase)) { + return + } + // Ignore variables + if (isVariable(valueLowerCase)) { + return + } + // Ignore keywords for other font parts + if (keywordSets.animationShorthandKeywords.has(valueLowerCase)) { + return + } + // Ignore numbers with units + const unit = getUnitFromValueNode(valueNode) + if (unit || unit === "") { + return + } + + animationNames.push(valueNode) + }) + + return animationNames +} diff --git a/src/utils/findAtRuleContext.js b/lib/utils/findAtRuleContext.js similarity index 64% rename from src/utils/findAtRuleContext.js rename to lib/utils/findAtRuleContext.js index 84ff3864b0..5246183d76 100644 --- a/src/utils/findAtRuleContext.js +++ b/lib/utils/findAtRuleContext.js @@ -1,3 +1,5 @@ +"use strict" + /** * Find the at-rule in which a rule is nested. * @@ -6,9 +8,12 @@ * @param {Rule} rule * @return {AtRule|null} */ -export default function findAtRuleContext(rule) { - const { parent } = rule - if (parent.type === "root") { return null } +module.exports = function findAtRuleContext(rule) { + const parent = rule.parent + + if (parent.type === "root") { + return null + } if (parent.type === "atrule") { return parent } diff --git a/src/utils/findFontFamily.js b/lib/utils/findFontFamily.js similarity index 52% rename from src/utils/findFontFamily.js rename to lib/utils/findFontFamily.js index 4dcd7304cd..1aab37cd6b 100644 --- a/src/utils/findFontFamily.js +++ b/lib/utils/findFontFamily.js @@ -1,22 +1,13 @@ -import { - basicKeywords, - fontFamilyKeywords, - fontShorthandKeywords, -} from "../reference/keywordSets" -import { - isNumbery, - isStandardSyntaxValue, - isValidFontSize, - isVariable, -} from "./" -import postcssValueParser from "postcss-value-parser" - -const nodeTypesToCheck = new Set([ - "word", - "string", - "space", - "div", -]) +"use strict" + +const keywordSets = require("../reference/keywordSets") +const isNumbery = require("./isNumbery") +const isStandardSyntaxValue = require("./isStandardSyntaxValue") +const isValidFontSize = require("./isValidFontSize") +const isVariable = require("./isVariable") +const postcssValueParser = require("postcss-value-parser") + +const nodeTypesToCheck = new Set([ "word", "string", "space", "div" ]) function joinValueNodes(firstNode, secondNode, charactersBetween) { firstNode.value = firstNode.value + charactersBetween + secondNode.value @@ -30,13 +21,13 @@ function joinValueNodes(firstNode, secondNode, charactersBetween) { * @param {string} value * @return {object} Collection font-family nodes */ -export default function findFontFamily(value) { +module.exports = function findFontFamily(value) { const fontFamilies = [] const valueNodes = postcssValueParser(value) // Handle `inherit`, `initial` and etc - if (valueNodes.nodes.length === 1 && basicKeywords.has(valueNodes.nodes[0].value.toLowerCase())) { + if (valueNodes.nodes.length === 1 && keywordSets.basicKeywords.has(valueNodes.nodes[0].value.toLowerCase())) { return [valueNodes.nodes[0]] } @@ -44,34 +35,47 @@ export default function findFontFamily(value) { let mergeCharacters = null valueNodes.walk((valueNode, index, nodes) => { - if (valueNode.type === "function") { return false } - if (!nodeTypesToCheck.has(valueNode.type)) { return } + if (valueNode.type === "function") { + return false + } + if (!nodeTypesToCheck.has(valueNode.type)) { + return + } const valueLowerCase = valueNode.value.toLowerCase() // Ignore non standard syntax - if (!isStandardSyntaxValue(valueLowerCase)) { return } + if (!isStandardSyntaxValue(valueLowerCase)) { + return + } // Ignore variables - if (isVariable(valueLowerCase)) { return } + if (isVariable(valueLowerCase)) { + return + } // Ignore keywords for other font parts - if (fontShorthandKeywords.has(valueLowerCase) && !fontFamilyKeywords.has(valueLowerCase)) { return } + if (keywordSets.fontShorthandKeywords.has(valueLowerCase) && !keywordSets.fontFamilyKeywords.has(valueLowerCase)) { + return + } // Ignore font-sizes - if (isValidFontSize(valueNode.value)) { return } + if (isValidFontSize(valueNode.value)) { + return + } // Ignore anything come after a /, because it's a line-height - if (nodes[index - 1] && nodes[index - 1].value === "/" - && nodes[index - 2] && isValidFontSize(nodes[index - 2].value)) { return } + if (nodes[index - 1] && nodes[index - 1].value === "/" && nodes[index - 2] && isValidFontSize(nodes[index - 2].value)) { + return + } // Ignore number values - if (isNumbery(valueLowerCase)) { return } + if (isNumbery(valueLowerCase)) { + return + } // Detect when a space or comma is dividing a list of font-families, and save the joining character. - if ((valueNode.type === "space" || (valueNode.type === "div" && valueNode.value !== ",")) - && fontFamilies.length !== 0 - ) { + if ((valueNode.type === "space" || valueNode.type === "div" && valueNode.value !== ",") && fontFamilies.length !== 0) { needMergeNodesByValue = true mergeCharacters = valueNode.value return diff --git a/lib/utils/findListStyleType.js b/lib/utils/findListStyleType.js new file mode 100644 index 0000000000..9ba04fd5b6 --- /dev/null +++ b/lib/utils/findListStyleType.js @@ -0,0 +1,51 @@ +"use strict" + +const isStandardSyntaxValue = require("./isStandardSyntaxValue") +const isVariable = require("./isVariable") +const keywordSets = require("../reference/keywordSets") +const postcssValueParser = require("postcss-value-parser") + +/** + * Get the list-style-type within a `list-style` shorthand property value. + * + * @param {string} value + * @return {object} Collection list-style-type nodes + */ +module.exports = function findListStyleType(value) { + const listStyleTypes = [] + + const valueNodes = postcssValueParser(value) + + // Handle `inherit`, `initial` and etc + if (valueNodes.nodes.length === 1 && keywordSets.listStyleTypeKeywords.has(valueNodes.nodes[0].value.toLowerCase())) { + return [valueNodes.nodes[0]] + } + + valueNodes.walk(valueNode => { + if (valueNode.type === "function") { + return false + } + if (valueNode.type !== "word") { + return + } + + const valueLowerCase = valueNode.value.toLowerCase() + + // Ignore non standard syntax + if (!isStandardSyntaxValue(valueLowerCase)) { + return + } + // Ignore variables + if (isVariable(valueLowerCase)) { + return + } + // Ignore keywords for other font parts + if (keywordSets.listStylePositionKeywords.has(valueLowerCase) || keywordSets.listStyleImageKeywords.has(valueLowerCase)) { + return + } + + listStyleTypes.push(valueNode) + }) + + return listStyleTypes +} diff --git a/src/utils/functionArgumentsSearch.js b/lib/utils/functionArgumentsSearch.js similarity index 75% rename from src/utils/functionArgumentsSearch.js rename to lib/utils/functionArgumentsSearch.js index 6ccff1b306..475ef7ee33 100644 --- a/src/utils/functionArgumentsSearch.js +++ b/lib/utils/functionArgumentsSearch.js @@ -1,5 +1,7 @@ -import balancedMatch from "balanced-match" -import styleSearch from "style-search" +"use strict" + +const balancedMatch = require("balanced-match") +const styleSearch = require("style-search") /** * Search a CSS string for functions by name. @@ -13,13 +15,15 @@ import styleSearch from "style-search" * matching function found, with the function's "argument(s) string" * and its starting index as the arguments */ -export default function (source, functionName, callback) { +module.exports = function (source, functionName, callback) { styleSearch({ source, target: functionName, functionNames: "check", }, match => { - if (source[match.endIndex] !== "(") { return } + if (source[match.endIndex] !== "(") { + return + } const parensMatch = balancedMatch("(", ")", source.substr(match.startIndex)) callback(parensMatch.body, match.endIndex + 1) }) diff --git a/lib/utils/getIsFileIgnored.js b/lib/utils/getIsFileIgnored.js new file mode 100644 index 0000000000..035e406297 --- /dev/null +++ b/lib/utils/getIsFileIgnored.js @@ -0,0 +1,14 @@ +"use strict" + +const ignore = require("ignore") +const multimatch = require("multimatch") +const path = require("path") + +module.exports = function getIsFileIgnored(ignorePatterns, ignoreFiles) { + const ignorePatternsFilter = ignore().add(ignorePatterns).createFilter() + + return file => { + const filepathRelativeToCwd = path.relative(process.cwd(), file) + return ignorePatternsFilter && !ignorePatternsFilter(filepathRelativeToCwd) || ignoreFiles && multimatch(file, ignoreFiles).length + } +} diff --git a/lib/utils/getModulePath.js b/lib/utils/getModulePath.js new file mode 100644 index 0000000000..5bfc2fe534 --- /dev/null +++ b/lib/utils/getModulePath.js @@ -0,0 +1,17 @@ +"use strict" + +const configurationError = require("./configurationError") +const resolveFrom = require("resolve-from") + +module.exports = function (basedir/*: string*/, lookup/*: string*/)/*: string*/ { + // First try to resolve from the provided directory, + // then try to resolve from process.cwd. + let path = resolveFrom(basedir, lookup) + if (!path) { + path = resolveFrom(process.cwd(), lookup) + } + if (!path) { + throw configurationError(`Could not find "${lookup}". Do you need a \`configBasedir\`?`) + } + return path +} diff --git a/lib/utils/getUnitFromValueNode.js b/lib/utils/getUnitFromValueNode.js new file mode 100644 index 0000000000..5b6ea2422e --- /dev/null +++ b/lib/utils/getUnitFromValueNode.js @@ -0,0 +1,38 @@ +"use strict" + +const blurInterpolation = require("./blurInterpolation") +const _ = require("lodash") +const isStandardSyntaxValue = require("./isStandardSyntaxValue") +const valueParser = require("postcss-value-parser") + +/** + * Get unit from value node + * + * Returns `null` if the unit is not found. + * + * @param {node} node + * @return {string|null} + */ +module.exports = function (node) { + if (!node || node && !node.value) { + return null + } + + const value = blurInterpolation(node.value, "") + // ignore hack unit + .replace("\\0", "").replace("\\9", "") + // ignore decimal place + .replace(".", "") + + if (node.type !== "word" || !isStandardSyntaxValue(value) || !_.isFinite(parseInt(value)) || node.value[0] === "#") { + return null + } + + const parsedUnit = valueParser.unit(value) + + if (!parsedUnit) { + return null + } + + return parsedUnit.unit +} diff --git a/src/utils/hasBlock.js b/lib/utils/hasBlock.js similarity index 69% rename from src/utils/hasBlock.js rename to lib/utils/hasBlock.js index 4234200dea..ab0c10f49d 100644 --- a/src/utils/hasBlock.js +++ b/lib/utils/hasBlock.js @@ -1,11 +1,11 @@ +"use strict" + /** * Check if a statement has an block (empty or otherwise). * * @param {Rule|AtRule} statement - postcss rule or at-rule node * @return {boolean} True if `statement` has a block (empty or otherwise) */ -export default function (statement) { - return ( - statement.nodes !== undefined - ) +module.exports = function (statement) { + return statement.nodes !== undefined } diff --git a/src/utils/hasEmptyBlock.js b/lib/utils/hasEmptyBlock.js similarity index 55% rename from src/utils/hasEmptyBlock.js rename to lib/utils/hasEmptyBlock.js index 93662ccd6d..4319a68f8e 100644 --- a/src/utils/hasEmptyBlock.js +++ b/lib/utils/hasEmptyBlock.js @@ -1,12 +1,12 @@ +"use strict" + /** * Check if a statement has an empty block. * * @param {Rule|AtRule} statement - postcss rule or at-rule node * @return {boolean} True if the statement has a block and it is empty */ -export default function (statement) { - return ( - statement.nodes !== undefined // has block - && statement.nodes.length === 0 // and is empty - ) +module.exports = function (statement) { + return statement.nodes !== undefined // has block + && statement.nodes.length === 0 // and is empty } diff --git a/lib/utils/hasEmptyLine.js b/lib/utils/hasEmptyLine.js new file mode 100644 index 0000000000..cb9d6ce4bb --- /dev/null +++ b/lib/utils/hasEmptyLine.js @@ -0,0 +1,11 @@ +"use strict" + +/** + * Check if a string contains at least one empty line + * + * @param {string} input + * @return {boolean} + */ +module.exports = function (string) { + return string && (string.indexOf("\n\n") !== -1 || string.indexOf("\n\r\n") !== -1) +} diff --git a/lib/utils/hasInterpolation.js b/lib/utils/hasInterpolation.js new file mode 100644 index 0000000000..d6aecf8d1c --- /dev/null +++ b/lib/utils/hasInterpolation.js @@ -0,0 +1,19 @@ +"use strict" + +const hasLessInterpolation = require("../utils/hasLessInterpolation") +const hasPsvInterpolation = require("../utils/hasPsvInterpolation") +const hasScssInterpolation = require("../utils/hasScssInterpolation") +/** + * Check whether a string has interpolation + * + * @param {string} string + * @return {boolean} If `true`, a string has interpolation + */ +module.exports = function (string) { + // SCSS or Less interpolation + if (hasLessInterpolation(string) || hasScssInterpolation(string) || hasPsvInterpolation(string)) { + return true + } + + return false +} diff --git a/src/utils/hasLessInterpolation.js b/lib/utils/hasLessInterpolation.js similarity index 62% rename from src/utils/hasLessInterpolation.js rename to lib/utils/hasLessInterpolation.js index a41e975500..4025f9ee7e 100644 --- a/src/utils/hasLessInterpolation.js +++ b/lib/utils/hasLessInterpolation.js @@ -1,11 +1,15 @@ +"use strict" + /** * Check whether a string has less interpolation * * @param {string} string * @return {boolean} If `true`, a string has less interpolation */ -export default function (string) { - if (/@{.+?}/.test(string)) { return true } +module.exports = function (string) { + if (/@{.+?}/.test(string)) { + return true + } return false } diff --git a/src/utils/hasPsvInterpolation.js b/lib/utils/hasPsvInterpolation.js similarity index 65% rename from src/utils/hasPsvInterpolation.js rename to lib/utils/hasPsvInterpolation.js index 541d004263..c0588bff91 100644 --- a/src/utils/hasPsvInterpolation.js +++ b/lib/utils/hasPsvInterpolation.js @@ -1,11 +1,15 @@ +"use strict" + /** * Check whether a string has postcss-simple-vars interpolation * * @param {string} string * @return {boolean} If `true`, a string has postcss-simple-vars interpolation */ -export default function (string) { - if (/\$\(.+?\)/.test(string)) { return true } +module.exports = function (string) { + if (/\$\(.+?\)/.test(string)) { + return true + } return false } diff --git a/src/utils/hasScssInterpolation.js b/lib/utils/hasScssInterpolation.js similarity index 62% rename from src/utils/hasScssInterpolation.js rename to lib/utils/hasScssInterpolation.js index 8d74247474..864f9df27d 100644 --- a/src/utils/hasScssInterpolation.js +++ b/lib/utils/hasScssInterpolation.js @@ -1,11 +1,15 @@ +"use strict" + /** * Check whether a string has scss interpolation * * @param {string} string * @return {boolean} If `true`, a string has scss interpolation */ -export default function (string) { - if (/#{.+?}/.test(string)) { return true } +module.exports = function (string) { + if (/#{.+?}/.test(string)) { + return true + } return false } diff --git a/src/utils/isAutoprefixable.js b/lib/utils/isAutoprefixable.js similarity index 76% rename from src/utils/isAutoprefixable.js rename to lib/utils/isAutoprefixable.js index 01e963cf54..e6a2f0f3f1 100644 --- a/src/utils/isAutoprefixable.js +++ b/lib/utils/isAutoprefixable.js @@ -1,6 +1,8 @@ -import Browsers from "autoprefixer/lib/browsers" -import Prefixes from "autoprefixer/lib/prefixes" -import autoprefixer from "autoprefixer" +"use strict" + +const Browsers = require("autoprefixer/lib/browsers") +const Prefixes = require("autoprefixer/lib/prefixes") +const autoprefixer = require("autoprefixer") /** * Use Autoprefixer's secret powers to determine whether or @@ -11,16 +13,13 @@ import autoprefixer from "autoprefixer" * vendor prefixes. */ -const prefixes = new Prefixes( - autoprefixer.data.prefixes, - new Browsers(autoprefixer.data.browsers, []) -) +const prefixes = new Prefixes(autoprefixer.data.prefixes, new Browsers(autoprefixer.data.browsers, [])) /** * Most identifier types have to be looked up in a unique way, * so we're exposing special functions for each. */ -export default { +module.exports = { atRuleName(identifier) { return prefixes.remove[`@${identifier.toLowerCase()}`] @@ -41,8 +40,7 @@ export default { }, propertyValue(prop, value) { - const possiblePrefixableValues = prefixes.remove[prop.toLowerCase()] - && prefixes.remove[prop.toLowerCase()].values + const possiblePrefixableValues = prefixes.remove[prop.toLowerCase()] && prefixes.remove[prop.toLowerCase()].values return possiblePrefixableValues && possiblePrefixableValues.some(valueObj => { return value.toLowerCase() === valueObj.prefixed }) diff --git a/lib/utils/isCounterIncrementCustomIdentValue.js b/lib/utils/isCounterIncrementCustomIdentValue.js new file mode 100644 index 0000000000..92d1176b59 --- /dev/null +++ b/lib/utils/isCounterIncrementCustomIdentValue.js @@ -0,0 +1,21 @@ +"use strict" + +const keywordSets = require("../reference/keywordSets") +const _ = require("lodash") + +/** + * Check value is a custom ident + * + * @param {string} value + * @return {boolean} If `true`, value is a custom ident + */ + +module.exports = function (value) { + const valueLowerCase = value.toLowerCase() + + if (keywordSets.counterIncrementKeywords.has(valueLowerCase) || _.isFinite(parseInt(valueLowerCase))) { + return false + } + + return true +} diff --git a/src/utils/isCustomMediaQuery.js b/lib/utils/isCustomMediaQuery.js similarity index 59% rename from src/utils/isCustomMediaQuery.js rename to lib/utils/isCustomMediaQuery.js index 6285d0268a..8b9e9069d9 100644 --- a/src/utils/isCustomMediaQuery.js +++ b/lib/utils/isCustomMediaQuery.js @@ -1,9 +1,11 @@ +"use strict" + /** * Check whether a media query is a custom * * @param {string} mediaQuery * @return {boolean} If `true`, media query is a custom */ -export default function (mediaQuery) { - return (mediaQuery.slice(0, 2) === "--") +module.exports = function (mediaQuery) { + return mediaQuery.slice(0, 2) === "--" } diff --git a/src/utils/isCustomProperty.js b/lib/utils/isCustomProperty.js similarity index 60% rename from src/utils/isCustomProperty.js rename to lib/utils/isCustomProperty.js index b821a67884..bef1a91d3b 100644 --- a/src/utils/isCustomProperty.js +++ b/lib/utils/isCustomProperty.js @@ -1,9 +1,11 @@ +"use strict" + /** * Check whether a property is a custom one * * @param {string} property * @return {boolean} If `true`, property is a custom one */ -export default function (property) { - return (property.slice(0, 2) === "--") +module.exports = function (property) { + return property.slice(0, 2) === "--" } diff --git a/lib/utils/isCustomPropertySet.js b/lib/utils/isCustomPropertySet.js new file mode 100644 index 0000000000..8814540ba1 --- /dev/null +++ b/lib/utils/isCustomPropertySet.js @@ -0,0 +1,16 @@ +"use strict" + +const _ = require("lodash") +const hasBlock = require("../utils/hasBlock") + +/** + * Check whether a property is a custom properties + * + * @param {string} rule + * @return {boolean} If `true`, property is a custom properties + */ +module.exports = function (rule) { + const selector = _.get(rule, "raws.selector.raw", rule.selector) + + return rule.type === "rule" && hasBlock(rule) && selector.slice(0, 2) === "--" && selector.slice(-1) === ":" +} diff --git a/lib/utils/isKeyframeRule.js b/lib/utils/isKeyframeRule.js new file mode 100644 index 0000000000..6a5d20a466 --- /dev/null +++ b/lib/utils/isKeyframeRule.js @@ -0,0 +1,13 @@ +"use strict" + +/** + * Check if a rule is a keyframe one + * + * @param {Rule} rule - postcss rule node + * @return {boolean} If `true`, the rule is a keyframe one + */ +module.exports = function (rule) { + const parent = rule.parent + + return parent.type === "atrule" && parent.name.toLowerCase() === "keyframes" +} diff --git a/lib/utils/isKeyframeSelector.js b/lib/utils/isKeyframeSelector.js new file mode 100644 index 0000000000..8d62994b75 --- /dev/null +++ b/lib/utils/isKeyframeSelector.js @@ -0,0 +1,22 @@ +"use strict" + +const keywordSets = require("../reference/keywordSets") + +/** + * Check whether a string is a keyframe selector. + * + * @param {string} selector + * @return {boolean} If `true`, the selector is a keyframe selector + */ +module.exports = function (selector) { + if (keywordSets.keyframeSelectorKeywords.has(selector)) { + return true + } + + // Percentages + if (/^(?:\d+\.?\d*|\d*\.?\d+)%$/.test(selector)) { + return true + } + + return false +} diff --git a/src/utils/isNumbery.js b/lib/utils/isNumbery.js similarity index 63% rename from src/utils/isNumbery.js rename to lib/utils/isNumbery.js index 9479c47e2f..279eed350a 100644 --- a/src/utils/isNumbery.js +++ b/lib/utils/isNumbery.js @@ -1,3 +1,5 @@ +"use strict" + /** * Check whether it's a number or a number-like string: * i.e. when coerced to a number it == itself. @@ -5,6 +7,6 @@ * @param {string} value * @return {boolean} If `true`, value is a number */ -export default function (value) { - return (value.trim().length !== 0 && Number(value) == value) +module.exports = function (value) { + return value.trim().length !== 0 && Number(value) == value } diff --git a/src/utils/isOnlyWhitespace.js b/lib/utils/isOnlyWhitespace.js similarity index 77% rename from src/utils/isOnlyWhitespace.js rename to lib/utils/isOnlyWhitespace.js index faa0a23cf9..abe83e3276 100644 --- a/src/utils/isOnlyWhitespace.js +++ b/lib/utils/isOnlyWhitespace.js @@ -1,4 +1,6 @@ -import isWhitespace from "./isWhitespace" +"use strict" + +const isWhitespace = require("./isWhitespace") /** * Returns a Boolean indicating whether the the input string is only whitespace. @@ -6,7 +8,7 @@ import isWhitespace from "./isWhitespace" * @param {string} input * @return {boolean} */ -export default function (input) { +module.exports = function (input) { let isOnlyWhitespace = true for (let i = 0, l = input.length; i < l; i++) { if (!isWhitespace(input[i])) { diff --git a/lib/utils/isRangeContextMediaFeature.js b/lib/utils/isRangeContextMediaFeature.js new file mode 100644 index 0000000000..e7b499a56e --- /dev/null +++ b/lib/utils/isRangeContextMediaFeature.js @@ -0,0 +1,11 @@ +"use strict" + +/** + * Check whether a media feature is a range context one + * + * @param {string} media feature + * @return {boolean} If `true`, media feature is a range context one + */ +module.exports = function (mediaFeature) { + return mediaFeature.indexOf("=") !== -1 || mediaFeature.indexOf("<") !== -1 || mediaFeature.indexOf(">") !== -1 +} diff --git a/src/utils/isSingleLineString.js b/lib/utils/isSingleLineString.js similarity index 78% rename from src/utils/isSingleLineString.js rename to lib/utils/isSingleLineString.js index 82236a2dea..f2cf674700 100644 --- a/src/utils/isSingleLineString.js +++ b/lib/utils/isSingleLineString.js @@ -1,3 +1,5 @@ +"use strict" + /** * Check if a string is a single line (i.e. does not contain * any newline characters). @@ -5,6 +7,6 @@ * @param {string} input * @return {boolean} */ -export default function (input) { +module.exports = function (input) { return !/[\n\r]/.test(input) } diff --git a/src/utils/isStandardSyntaxAtRule.js b/lib/utils/isStandardSyntaxAtRule.js similarity index 66% rename from src/utils/isStandardSyntaxAtRule.js rename to lib/utils/isStandardSyntaxAtRule.js index 4600d4ee9d..82515c6bdf 100644 --- a/src/utils/isStandardSyntaxAtRule.js +++ b/lib/utils/isStandardSyntaxAtRule.js @@ -1,15 +1,21 @@ +"use strict" + /** * Check whether a at-rule is standard * * @param {atRule} postcss at-rule node * @return {boolean} If `true`, the declaration is standard */ -export default function (atRule: postcss$atRule): boolean { +module.exports = function (atRule/*: postcss$atRule*/)/*: boolean*/ { // Ignore scss `@content` inside mixins - if (!atRule.nodes && atRule.params === "") { return false } + if (!atRule.nodes && atRule.params === "") { + return false + } // Ignore detached ruleset `@detached-ruleset: { background: red; }; .top { @detached-ruleset(); }` - if (!atRule.nodes && atRule.raws.afterName === "" && atRule.params[0] === "(") { return false } + if (!atRule.nodes && atRule.raws.afterName === "" && atRule.params[0] === "(") { + return false + } return true } diff --git a/src/utils/isStandardSyntaxDeclaration.js b/lib/utils/isStandardSyntaxDeclaration.js similarity index 60% rename from src/utils/isStandardSyntaxDeclaration.js rename to lib/utils/isStandardSyntaxDeclaration.js index a99d0bd810..e9881f3fa6 100644 --- a/src/utils/isStandardSyntaxDeclaration.js +++ b/lib/utils/isStandardSyntaxDeclaration.js @@ -1,23 +1,35 @@ +"use strict" + /** * Check whether a declaration is standard * * @param {Decl} postcss declaration node * @return {boolean} If `true`, the declaration is standard */ -export default function (decl) { - const { prop, parent } = decl +module.exports = function (decl) { + const prop = decl.prop, + parent = decl.parent // Declarations belong in a declaration block - if (parent.type === "root") { return false } + + if (parent.type === "root") { + return false + } // SCSS var (e.g. $var: x), nested list (e.g. $list: (x)) or nested map (e.g. $map: (key:value)) - if (prop[0] === "$") { return false } + if (prop[0] === "$") { + return false + } // Less var (e.g. @var: x), but exclude variable interpolation (e.g. @{var}) - if (prop[0] === "@" && prop[1] !== "{") { return false } + if (prop[0] === "@" && prop[1] !== "{") { + return false + } // SCSS nested properties (e.g. border: { style: solid; color: red; }) - if (parent.selector && parent.selector[parent.selector.length - 1] === ":" && parent.selector.substring(0, 2) !== "--") { return false } + if (parent.selector && parent.selector[parent.selector.length - 1] === ":" && parent.selector.substring(0, 2) !== "--") { + return false + } return true } diff --git a/src/utils/isStandardSyntaxFunction.js b/lib/utils/isStandardSyntaxFunction.js similarity index 74% rename from src/utils/isStandardSyntaxFunction.js rename to lib/utils/isStandardSyntaxFunction.js index 1e73b56cc8..1a2d6354a9 100644 --- a/src/utils/isStandardSyntaxFunction.js +++ b/lib/utils/isStandardSyntaxFunction.js @@ -1,12 +1,16 @@ +"use strict" + /** * Check whether a function is standard * * @param {Node} postcss-value-parser node (of type function) * @return {boolean} If `true`, the function is standard */ -export default function (node) { +module.exports = function (node) { // Function nodes without names are things in parentheses like Sass lists - if (!node.value) { return false } + if (!node.value) { + return false + } return true } diff --git a/src/utils/isStandardSyntaxMediaFeature.js b/lib/utils/isStandardSyntaxMediaFeature.js similarity index 58% rename from src/utils/isStandardSyntaxMediaFeature.js rename to lib/utils/isStandardSyntaxMediaFeature.js index 79f2003d33..a626bba6ca 100644 --- a/src/utils/isStandardSyntaxMediaFeature.js +++ b/lib/utils/isStandardSyntaxMediaFeature.js @@ -1,19 +1,25 @@ -import { hasInterpolation } from "../utils" +"use strict" + +const hasInterpolation = require("../utils/hasInterpolation") /** * Check whether a media feature is standard * * @param {string} mediaFeature * @return {boolean} If `true`, the media feature is standard */ -export default function (mediaFeature) { +module.exports = function (mediaFeature) { // Remove outside parens mediaFeature = mediaFeature.slice(1, -1) // Parentheticals used for non-standard operations e.g. ($var - 10) - if (mediaFeature.indexOf("(") !== -1) { return false } + if (mediaFeature.indexOf("(") !== -1) { + return false + } // SCSS or Less interpolation - if (hasInterpolation(mediaFeature)) { return false } + if (hasInterpolation(mediaFeature)) { + return false + } return true } diff --git a/src/utils/isStandardSyntaxMediaFeatureName.js b/lib/utils/isStandardSyntaxMediaFeatureName.js similarity index 61% rename from src/utils/isStandardSyntaxMediaFeatureName.js rename to lib/utils/isStandardSyntaxMediaFeatureName.js index 1e47abc99d..4f2deb7b52 100644 --- a/src/utils/isStandardSyntaxMediaFeatureName.js +++ b/lib/utils/isStandardSyntaxMediaFeatureName.js @@ -1,12 +1,16 @@ +"use strict" + /** * Check whether a media feature name is standard * * @param {string} media feature name * @return {boolean} If `true`, the media feature name is standard */ -export default function (mediaFeatureName) { +module.exports = function (mediaFeatureName) { // SCSS interpolation - if (/#{.+?}|\$.+?/.test(mediaFeatureName)) { return false } + if (/#{.+?}|\$.+?/.test(mediaFeatureName)) { + return false + } return true } diff --git a/src/utils/isStandardSyntaxProperty.js b/lib/utils/isStandardSyntaxProperty.js similarity index 52% rename from src/utils/isStandardSyntaxProperty.js rename to lib/utils/isStandardSyntaxProperty.js index a272c4b341..41dd7a8942 100644 --- a/src/utils/isStandardSyntaxProperty.js +++ b/lib/utils/isStandardSyntaxProperty.js @@ -1,19 +1,27 @@ -import { hasInterpolation } from "../utils" +"use strict" + +const hasInterpolation = require("../utils/hasInterpolation") /** * Check whether a property is standard * * @param {string} property * @return {boolean} If `true`, the property is standard */ -export default function (property) { +module.exports = function (property) { // SCSS var (e.g. $var: x), list (e.g. $list: (x)) or map (e.g. $map: (key:value)) - if (property[0] === "$") { return false } + if (property[0] === "$") { + return false + } // Less var (e.g. @var: x) - if (property[0] === "@") { return false } + if (property[0] === "@") { + return false + } // SCSS or Less interpolation - if (hasInterpolation(property)) { return false } + if (hasInterpolation(property)) { + return false + } return true } diff --git a/src/utils/isStandardSyntaxRule.js b/lib/utils/isStandardSyntaxRule.js similarity index 63% rename from src/utils/isStandardSyntaxRule.js rename to lib/utils/isStandardSyntaxRule.js index 0f2820b754..dff8711dac 100644 --- a/src/utils/isStandardSyntaxRule.js +++ b/lib/utils/isStandardSyntaxRule.js @@ -1,5 +1,7 @@ -import _ from "lodash" -import { isCustomPropertySet } from "../utils" +"use strict" + +const _ = require("lodash") +const isCustomPropertySet = require("../utils/isCustomPropertySet") /** * Check whether a rule is standard @@ -7,28 +9,40 @@ import { isCustomPropertySet } from "../utils" * @param {Rule} postcss rule node * @return {boolean} If `true`, the rule is standard */ -export default function (rule) { +module.exports = function (rule) { // Get full selector const selector = _.get(rule, "raws.selector.raw", rule.selector) // Custom property set (e.g. --custom-property-set: {}) - if (isCustomPropertySet(rule)) { return false } + if (isCustomPropertySet(rule)) { + return false + } // Called Less mixin (e.g. a { .mixin() }) - if (rule.ruleWithoutBody) { return false } + if (rule.ruleWithoutBody) { + return false + } // Less detached rulesets - if (selector.slice(0, 1) === "@" && selector.slice(-1) === ":") { return false } + if (selector.slice(0, 1) === "@" && selector.slice(-1) === ":") { + return false + } // Ignore mixin or &:extend rule // https://github.com/webschik/postcss-less/blob/master/lib/less-parser.js#L52 - if (rule.params && rule.params[0]) { return false } + if (rule.params && rule.params[0]) { + return false + } // Non-outputting Less mixin definition (e.g. .mixin() {}) - if (_.endsWith(selector, ")") && !_.includes(selector, ":")) { return false } + if (_.endsWith(selector, ")") && !_.includes(selector, ":")) { + return false + } // Ignore Scss nested properties - if (selector.slice(-1) === ":") { return false } + if (selector.slice(-1) === ":") { + return false + } return true } diff --git a/lib/utils/isStandardSyntaxSelector.js b/lib/utils/isStandardSyntaxSelector.js new file mode 100644 index 0000000000..4da59161df --- /dev/null +++ b/lib/utils/isStandardSyntaxSelector.js @@ -0,0 +1,22 @@ +"use strict" + +const hasInterpolation = require("../utils/hasInterpolation") +/** + * Check whether a selector is standard + * + * @param {string} selector + * @return {boolean} If `true`, the selector is standard + */ +module.exports = function (selector) { + // SCSS or Less interpolation + if (hasInterpolation(selector)) { + return false + } + + // SCSS placeholder selectors + if (selector.indexOf("%") === 0) { + return false + } + + return true +} diff --git a/src/utils/isStandardSyntaxTypeSelector.js b/lib/utils/isStandardSyntaxTypeSelector.js similarity index 53% rename from src/utils/isStandardSyntaxTypeSelector.js rename to lib/utils/isStandardSyntaxTypeSelector.js index 29511e2e90..b711765895 100644 --- a/src/utils/isStandardSyntaxTypeSelector.js +++ b/lib/utils/isStandardSyntaxTypeSelector.js @@ -1,3 +1,5 @@ +"use strict" + /** * Check whether a type selector is standard * @@ -5,29 +7,28 @@ * @return {boolean} If `true`, the type selector is standard */ -import { - aNPlusBNotationPseudoClasses, - linguisticPseudoClasses, -} from "../reference/keywordSets" +const keywordSets = require("../reference/keywordSets") -export default function (node) { +module.exports = function (node) { // postcss-selector-parser includes the arguments to nth-child() functions // as "tags", so we need to ignore them ourselves. // The fake-tag's "parent" is actually a selector node, whose parent // should be the :nth-child pseudo node. - const { parent: { parent: { type: parentType, value: parentValue } } } = node + const _node$parent$parent = node.parent.parent + const parentType = _node$parent$parent.type, + parentValue = _node$parent$parent.value + if (parentValue) { const normalisedParentName = parentValue.toLowerCase().replace(/:+/, "") - if ( - parentType === "pseudo" && ( - aNPlusBNotationPseudoClasses.has(normalisedParentName) - || linguisticPseudoClasses.has(normalisedParentName) - ) - ) { return false } + if (parentType === "pseudo" && (keywordSets.aNPlusBNotationPseudoClasses.has(normalisedParentName) || keywordSets.linguisticPseudoClasses.has(normalisedParentName))) { + return false + } } // &-bar is a nesting selector combined with a suffix - if (node.prev() && node.prev().type === "nesting") { return false } + if (node.prev() && node.prev().type === "nesting") { + return false + } return true } diff --git a/lib/utils/isStandardSyntaxUrl.js b/lib/utils/isStandardSyntaxUrl.js new file mode 100644 index 0000000000..eea32ebb8c --- /dev/null +++ b/lib/utils/isStandardSyntaxUrl.js @@ -0,0 +1,47 @@ +"use strict" + +const hasLessInterpolation = require("../utils/hasLessInterpolation") +const hasPsvInterpolation = require("../utils/hasPsvInterpolation") +const hasScssInterpolation = require("../utils/hasScssInterpolation") + +/** + * Check whether a URL is standard + * + * @param {string} url + * @return {boolean} If `true`, the url is standard + */ +module.exports = function (url) { + if (url.length === 0) { + return true + } + + // Sass interpolation works anywhere + if (hasScssInterpolation(url) || hasPsvInterpolation(url)) { + return false + } + + // Inside `'` and `"` work only LESS interpolation + if (url[0] === "'" && url[url.length - 1] === "'" || url[0] === "\"" && url[url.length - 1] === "\"") { + if (hasLessInterpolation(url)) { + return false + } + + return true + } + + // Less variable works only at the beginning + // Check is less variable, allow use '@url/some/path' + // https://github.com/less/less.js/blob/3.x/lib/less/parser/parser.js#L547 + if (url[0] === "@" && /^@@?[\w-]+$/.test(url)) { + return false + } + + // In url without quotes scss variable can be everywhere + // But in this case it is allowed to use only specific characters + // Also forbidden "/" at the end of url + if (url.indexOf("$") !== -1 && /^[\$\sA-Za-z0-9+-/*_'"\/]+$/.test(url) && url[url.length - 1] !== "/") { + return false + } + + return true +} diff --git a/lib/utils/isStandardSyntaxValue.js b/lib/utils/isStandardSyntaxValue.js new file mode 100644 index 0000000000..7ac0cc666f --- /dev/null +++ b/lib/utils/isStandardSyntaxValue.js @@ -0,0 +1,27 @@ +"use strict" + +const hasInterpolation = require("../utils/hasInterpolation") +/** + * Check whether a value is standard + * + * @param {string} value + * @return {boolean} If `true`, the value is a variable + */ +module.exports = function (value) { + // SCSS variable + if (value[0] === "$") { + return false + } + + // Less variable + if (value[0] === "@") { + return false + } + + // SCSS or Less interpolation + if (hasInterpolation(value)) { + return false + } + + return true +} diff --git a/lib/utils/isValidFontSize.js b/lib/utils/isValidFontSize.js new file mode 100644 index 0000000000..2cf234588a --- /dev/null +++ b/lib/utils/isValidFontSize.js @@ -0,0 +1,36 @@ +"use strict" + +const keywordSets = require("../reference/keywordSets") +const valueParser = require("postcss-value-parser") + +/** + * Check if a word is a font-size value. + * + * @param {string} word + * @return {boolean} + */ +module.exports = function (word) { + if (!word) { + return false + } + + if (keywordSets.fontSizeKeywords.has(word)) { + return true + } + + const numberUnit = valueParser.unit(word) + if (!numberUnit) { + return false + } + + const unit = numberUnit.unit + + if (unit === "%") { + return true + } + if (keywordSets.lengthUnits.has(unit.toLowerCase())) { + return true + } + + return false +} diff --git a/src/utils/isValidHex.js b/lib/utils/isValidHex.js similarity index 51% rename from src/utils/isValidHex.js rename to lib/utils/isValidHex.js index c0e04083c1..6bf02261f0 100644 --- a/src/utils/isValidHex.js +++ b/lib/utils/isValidHex.js @@ -1,9 +1,12 @@ +"use strict" + /** * Check if a value is a valid 3, 4, 6 or 8 digit hex * * @param {string} value * @return {boolean} If `true`, the hex is valid */ -export default function (value) { - return /^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value) +module.exports = function (value) { + return (/^#(?:[0-9a-fA-F]{3,4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value) + ) } diff --git a/src/utils/isVariable.js b/lib/utils/isVariable.js similarity index 61% rename from src/utils/isVariable.js rename to lib/utils/isVariable.js index e4baab4334..41127281c1 100644 --- a/src/utils/isVariable.js +++ b/lib/utils/isVariable.js @@ -1,9 +1,11 @@ +"use strict" + /** * Check whether a word is a variable i.e var(--custom-property). * * @param {string} word * @return {boolean} If `true`, the word is a variable */ -export default function (word) { - return (word.toLowerCase().slice(0, 4) === "var(") +module.exports = function (word) { + return word.toLowerCase().slice(0, 4) === "var(" } diff --git a/src/utils/isWhitespace.js b/lib/utils/isWhitespace.js similarity index 78% rename from src/utils/isWhitespace.js rename to lib/utils/isWhitespace.js index fb2e2b1402..4df7523334 100644 --- a/src/utils/isWhitespace.js +++ b/lib/utils/isWhitespace.js @@ -1,9 +1,11 @@ +"use strict" + /** * Check if a character is whitespace. * * @param {string} char - A single character * @return {boolean} */ -export default function (char) { +module.exports = function (char) { return [ " ", "\n", "\t", "\r", "\f" ].indexOf(char) !== -1 } diff --git a/src/utils/matchesStringOrRegExp.js b/lib/utils/matchesStringOrRegExp.js similarity index 80% rename from src/utils/matchesStringOrRegExp.js rename to lib/utils/matchesStringOrRegExp.js index bcfbc2a40e..433e2e2275 100644 --- a/src/utils/matchesStringOrRegExp.js +++ b/lib/utils/matchesStringOrRegExp.js @@ -1,3 +1,5 @@ +"use strict" + /** * Compares a string to a second value that, if it fits a certain convention, * is converted to a regular expression before the comparison. @@ -13,7 +15,7 @@ * - `match`: the `input` value that had a match * - `pattern`: the `comparison` pattern that had a match */ -export default function matchesStringOrRegExp(input, comparison) { +module.exports = function matchesStringOrRegExp(input, comparison) { if (!Array.isArray(input)) { return testAgainstStringOrArray(input, comparison) } @@ -43,17 +45,12 @@ function testAgainstStringOrArray(value, comparison) { } function testAgainstString(value, comparison) { - const comparisonIsRegex = comparison[0] === "/" - && comparison[comparison.length - 1] === "/" + const comparisonIsRegex = comparison[0] === "/" && comparison[comparison.length - 1] === "/" if (comparisonIsRegex) { const valueMatches = new RegExp(comparison.slice(1, -1)).test(value) - return (valueMatches) - ? { match: value, pattern: comparison } - : false + return valueMatches ? { match: value, pattern: comparison } : false } - return (value === comparison) - ? { match: value, pattern: comparison } - : false + return value === comparison ? { match: value, pattern: comparison } : false } diff --git a/src/utils/nextNonCommentNode.js b/lib/utils/nextNonCommentNode.js similarity index 83% rename from src/utils/nextNonCommentNode.js rename to lib/utils/nextNonCommentNode.js index 6a008ebbd2..99f0d62eb9 100644 --- a/src/utils/nextNonCommentNode.js +++ b/lib/utils/nextNonCommentNode.js @@ -1,3 +1,5 @@ +"use strict" + /** * Get the next non-comment node in a PostCSS AST * at or after a given node. @@ -6,7 +8,7 @@ * @return {undefined|Node} The next non-comment node, * or `null` if that doesn't not exist. */ -export default function nextNonCommentNode(startNode) { +module.exports = function nextNonCommentNode(startNode) { if (!startNode || !startNode.next) return null if (startNode.type === "comment") { diff --git a/src/utils/nodeContextLookup.js b/lib/utils/nodeContextLookup.js similarity index 86% rename from src/utils/nodeContextLookup.js rename to lib/utils/nodeContextLookup.js index 5151e73b26..3898cb4e8f 100644 --- a/src/utils/nodeContextLookup.js +++ b/lib/utils/nodeContextLookup.js @@ -1,3 +1,5 @@ +"use strict" + /** * Create a collection of Maps that serve to contextualize a given node. * This is useful to ensure that you only compare nodes that share a certain @@ -8,13 +10,15 @@ * * For a usage example, see `selector-no-descending-specificity`. */ -export default function () { +module.exports = function () { const contextMap = new Map() return { - getContext(node, ...subContexts) { + getContext(node) { const nodeSource = node.source.input.from const baseContext = creativeGetMap(contextMap, nodeSource) + const subContexts = Array.from(arguments).slice(1) + return subContexts.reduce((result, context) => { return creativeGetMap(result, context) }, baseContext) diff --git a/src/utils/optionsMatches.js b/lib/utils/optionsMatches.js similarity index 50% rename from src/utils/optionsMatches.js rename to lib/utils/optionsMatches.js index 52ccc03ecd..a46bcda2f6 100644 --- a/src/utils/optionsMatches.js +++ b/lib/utils/optionsMatches.js @@ -1,4 +1,6 @@ -import matchesStringOrRegExp from "./matchesStringOrRegExp" +"use strict" + +const matchesStringOrRegExp = require("./matchesStringOrRegExp") /** * Check if an options object's propertyName contains a user-defined string or @@ -9,11 +11,6 @@ import matchesStringOrRegExp from "./matchesStringOrRegExp" * @param {string} input - The needle * @return {boolean} If `true`, a match was found */ -export default function optionsMatches(options, propertyName, input) { - return !!( - options && - options[propertyName] && - typeof input === "string" && - matchesStringOrRegExp(input.toLowerCase(), options[propertyName]) - ) +module.exports = function optionsMatches(options, propertyName, input) { + return !!(options && options[propertyName] && typeof input === "string" && matchesStringOrRegExp(input.toLowerCase(), options[propertyName])) } diff --git a/lib/utils/parseSelector.js b/lib/utils/parseSelector.js new file mode 100644 index 0000000000..29b4164d12 --- /dev/null +++ b/lib/utils/parseSelector.js @@ -0,0 +1,11 @@ +"use strict" + +const selectorParser = require("postcss-selector-parser") + +module.exports = function (selector, result, node, cb) { + try { + selectorParser(cb).process(selector) + } catch (e) { + result.warn("Cannot parse selector", { node }) + } +} diff --git a/src/utils/rawNodeString.js b/lib/utils/rawNodeString.js similarity index 84% rename from src/utils/rawNodeString.js rename to lib/utils/rawNodeString.js index 807721fb49..31ff3a3380 100644 --- a/src/utils/rawNodeString.js +++ b/lib/utils/rawNodeString.js @@ -1,10 +1,12 @@ +"use strict" + /** * Stringify PostCSS node including its raw "before" string. * * @param {Node} node - Any PostCSS node * @return {string} */ -export default function (node) { +module.exports = function (node) { let result = "" if (node.raws.before) { result += node.raws.before diff --git a/src/utils/report.js b/lib/utils/report.js similarity index 64% rename from src/utils/report.js rename to lib/utils/report.js index 6b81b8d26e..391902f83e 100644 --- a/src/utils/report.js +++ b/lib/utils/report.js @@ -1,4 +1,6 @@ -import { get } from "lodash" +"use strict" + +const _ = require("lodash") /** * Report a violation. @@ -21,15 +23,15 @@ import { get } from "lodash" * @param {Node} [violation.word] - Word that should be passed to result.warn() * @param {number} [violation.line] - Line number of the violation */ -export default function ({ - ruleName, - result, - message, - line, - node, - index, - word, -}) { +module.exports = function (violation) { + const ruleName = violation.ruleName + const result = violation.result + const message = violation.message + const line = violation.line + const node = violation.node + const index = violation.index + const word = violation.word + result.stylelint = result.stylelint || {} // In quiet mode, mere warnings are ignored @@ -45,17 +47,16 @@ export default function ({ const ranges = result.stylelint.disabledRanges[ruleName] || result.stylelint.disabledRanges.all for (const range of ranges) { if ( - // If the violation is within a disabledRange, - // and that disabledRange's rules include this one, - // do not register a warning - range.start <= startLine - && (range.end >= startLine || range.end === undefined) - && (!range.rules || range.rules.indexOf(ruleName) !== -1) - ) { return } + // If the violation is within a disabledRange, + // and that disabledRange's rules include this one, + // do not register a warning + range.start <= startLine && (range.end >= startLine || range.end === undefined) && (!range.rules || range.rules.indexOf(ruleName) !== -1)) { + return + } } } - const severity = get(result.stylelint, [ "ruleSeverities", ruleName ], "ignore") + const severity = _.get(result.stylelint, [ "ruleSeverities", ruleName ], "ignore") if (!result.stylelint.stylelintError && severity === "error") { result.stylelint.stylelintError = true @@ -65,10 +66,16 @@ export default function ({ severity, rule: ruleName, } - if (node) { warningProperties.node = node } - if (index) { warningProperties.index = index } - if (word) { warningProperties.word = word } + if (node) { + warningProperties.node = node + } + if (index) { + warningProperties.index = index + } + if (word) { + warningProperties.word = word + } - const warningMessage = get(result.stylelint, [ "customMessages", ruleName ], message) + const warningMessage = _.get(result.stylelint, [ "customMessages", ruleName ], message) result.warn(warningMessage, warningProperties) } diff --git a/src/utils/ruleMessages.js b/lib/utils/ruleMessages.js similarity index 64% rename from src/utils/ruleMessages.js rename to lib/utils/ruleMessages.js index a09f533d14..3ecffa4926 100644 --- a/src/utils/ruleMessages.js +++ b/lib/utils/ruleMessages.js @@ -1,3 +1,5 @@ +"use strict" + /** * Given an object of violation messages, return another * that provides the same messages postfixed with the rule @@ -8,12 +10,16 @@ * and values are either message strings or functions that return message strings * @return {object} New message object, whose messages will be marked with the rule name */ -export default function (ruleName, messages) { +module.exports = function (ruleName, messages) { return Object.keys(messages).reduce((newMessages, messageId) => { const messageText = messages[messageId] - newMessages[messageId] = (typeof messageText === "string") - ? `${messageText} (${ruleName})` - : (...args) => `${messageText(...args)} (${ruleName})` + if (typeof messageText === "string") { + newMessages[messageId] = `${messageText} (${ruleName})` + } else { + newMessages[messageId] = function () { + return `${messageText.apply(null, arguments)} (${ruleName})` + } + } return newMessages }, {}) } diff --git a/src/utils/validateObjectWithStringArrayProps.js b/lib/utils/validateObjectWithStringArrayProps.js similarity index 66% rename from src/utils/validateObjectWithStringArrayProps.js rename to lib/utils/validateObjectWithStringArrayProps.js index 00b5f66ece..5311ff1fd8 100644 --- a/src/utils/validateObjectWithStringArrayProps.js +++ b/lib/utils/validateObjectWithStringArrayProps.js @@ -1,4 +1,6 @@ -import _ from "lodash" +"use strict" + +const _ = require("lodash") /** * Check whether the variable is an object and all it's properties are arrays of string values: @@ -13,13 +15,17 @@ import _ from "lodash" * @return {boolean} If `true`, all object's properties are arrays of string values */ -export default function (value) { - if (!_.isPlainObject(value)) { return false } +module.exports = function (value) { + if (!_.isPlainObject(value)) { + return false + } return Object.keys(value).every(key => { - if (!_.isArray(value[key])) { return false } + if (!_.isArray(value[key])) { + return false + } // Make sure the array items are strings - return value[key].every(item => (_.isString(item))) + return value[key].every(item => _.isString(item)) }) } diff --git a/src/utils/validateOptions.js b/lib/utils/validateOptions.js similarity index 68% rename from src/utils/validateOptions.js rename to lib/utils/validateOptions.js index 3458e47c0d..267c92e93b 100644 --- a/src/utils/validateOptions.js +++ b/lib/utils/validateOptions.js @@ -1,9 +1,8 @@ -import _ from "lodash" +"use strict" -const ignoredOptions = [ - "severity", - "message", -] +const _ = require("lodash") + +const ignoredOptions = [ "severity", "message" ] /** * Validate a rule's options. @@ -25,10 +24,12 @@ const ignoredOptions = [ * - `optional` (optional): If this is `true`, `actual` can be undefined. * @return {boolean} Whether or not the options are valid (true = valid) */ -export default function (result, ruleName, ...optionDescriptions) { +module.exports = function (result, ruleName) { let noErrors = true - optionDescriptions.forEach((optionDescription) => { + const optionDescriptions = Array.from(arguments).slice(2) + + optionDescriptions.forEach(optionDescription => { validate(optionDescription, ruleName, complain) }) @@ -43,16 +44,25 @@ export default function (result, ruleName, ...optionDescriptions) { return noErrors } -function validate({ possible, actual, optional }, ruleName, complain) { - if (actual === null || _.isEqual(actual, [null])) { return } +function validate(opts, ruleName, complain) { + const possible = opts.possible + const actual = opts.actual + const optional = opts.optional - const nothingPossible = possible === undefined - || (Array.isArray(possible) && possible.length === 0) + if (actual === null || _.isEqual(actual, [null])) { + return + } - if (nothingPossible && actual === true) { return } + const nothingPossible = possible === undefined || Array.isArray(possible) && possible.length === 0 + + if (nothingPossible && actual === true) { + return + } if (actual === undefined) { - if (nothingPossible || optional) { return } + if (nothingPossible || optional) { + return + } complain(`Expected option value for rule "${ruleName}"`) return } else if (nothingPossible) { @@ -71,7 +81,9 @@ function validate({ possible, actual, optional }, ruleName, complain) { // If `possible` is an array instead of an object ... if (!_.isPlainObject(possible)) { [].concat(actual).forEach(a => { - if (isValid(possible, a)) { return } + if (isValid(possible, a)) { + return + } complain(`Invalid option value "${a}" for rule "${ruleName}"`) }) return @@ -79,25 +91,24 @@ function validate({ possible, actual, optional }, ruleName, complain) { // If possible is an object ... if (!_.isPlainObject(actual)) { - complain( - `Invalid option value ${JSON.stringify(actual)} for rule "${ruleName}": ` + - "should be an object" - ) + complain(`Invalid option value ${JSON.stringify(actual)} for rule "${ruleName}": ` + "should be an object") return } Object.keys(actual).forEach(optionName => { - if (ignoredOptions.indexOf(optionName) !== -1) { return } + if (ignoredOptions.indexOf(optionName) !== -1) { + return + } if (!possible[optionName]) { complain(`Invalid option name "${optionName}" for rule "${ruleName}"`) return } - const actualOptionValue = actual[optionName] - - ;[].concat(actualOptionValue).forEach(a => { - if (isValid(possible[optionName], a)) { return } + const actualOptionValue = actual[optionName];[].concat(actualOptionValue).forEach(a => { + if (isValid(possible[optionName], a)) { + return + } complain(`Invalid value "${a}" for option "${optionName}" of rule "${ruleName}"`) }) }) @@ -107,7 +118,11 @@ function isValid(possible, actual) { const possibleList = [].concat(possible) for (let i = 0, l = possibleList.length; i < l; i++) { const possibility = possibleList[i] - if (typeof possibility === "function" && possibility(actual)) { return true } - if (actual === possibility) { return true } + if (typeof possibility === "function" && possibility(actual)) { + return true + } + if (actual === possibility) { + return true + } } } diff --git a/src/utils/whitespaceChecker.js b/lib/utils/whitespaceChecker.js similarity index 66% rename from src/utils/whitespaceChecker.js rename to lib/utils/whitespaceChecker.js index b2da13e8f0..5062f47c73 100644 --- a/src/utils/whitespaceChecker.js +++ b/lib/utils/whitespaceChecker.js @@ -1,7 +1,8 @@ -import { assign } from "lodash" -import configurationError from "./configurationError" -import isSingleLineString from "./isSingleLineString" -import isWhitespace from "./isWhitespace" +"use strict" + +const configurationError = require("./configurationError") +const isSingleLineString = require("./isSingleLineString") +const isWhitespace = require("./isWhitespace") /** * Create a whitespaceChecker, which exposes the following functions: @@ -31,7 +32,7 @@ import isWhitespace from "./isWhitespace" * @param {function} [messages.rejectedBeforeMultiLine] * @return {object} The checker, with its exposed checking functions */ -export default function (targetWhitespace, expectation, messages) { +module.exports = function (targetWhitespace, expectation, messages) { // Keep track of active arguments in order to avoid passing // too much stuff around, making signatures long and confusing. // This variable gets reset anytime a checking function is called. @@ -62,15 +63,15 @@ export default function (targetWhitespace, expectation, messages) { * With this option, the checker will see if a newline *begins* the whitespace before * the `index`. */ - function before({ - source, - index, - err, - errTarget, - lineCheckStr, - onlyOneChar = false, - allowIndentation = false, - }) { + function before(args) { + const source = args.source + const index = args.index + const err = args.err + const errTarget = args.errTarget + const lineCheckStr = args.lineCheckStr + const onlyOneChar = args.onlyOneChar === undefined ? false : args.onlyOneChar + const allowIndentation = args.allowIndentation === undefined ? false : args.allowIndentation + activeArgs = { source, index, err, errTarget, onlyOneChar, allowIndentation } switch (expectation) { case "always": @@ -80,19 +81,27 @@ export default function (targetWhitespace, expectation, messages) { rejectBefore() break case "always-single-line": - if (!isSingleLineString(lineCheckStr || source)) { return } + if (!isSingleLineString(lineCheckStr || source)) { + return + } expectBefore(messages.expectedBeforeSingleLine) break case "never-single-line": - if (!isSingleLineString(lineCheckStr || source)) { return } + if (!isSingleLineString(lineCheckStr || source)) { + return + } rejectBefore(messages.rejectedBeforeSingleLine) break case "always-multi-line": - if (isSingleLineString(lineCheckStr || source)) { return } + if (isSingleLineString(lineCheckStr || source)) { + return + } expectBefore(messages.expectedBeforeMultiLine) break case "never-multi-line": - if (isSingleLineString(lineCheckStr || source)) { return } + if (isSingleLineString(lineCheckStr || source)) { + return + } rejectBefore(messages.rejectedBeforeMultiLine) break default: @@ -106,7 +115,14 @@ export default function (targetWhitespace, expectation, messages) { * Parameters are pretty much the same as for `before()`, above, just substitute * the word "after" for "before". */ - function after({ source, index, err, errTarget, lineCheckStr, onlyOneChar = false }) { + function after(args) { + const source = args.source + const index = args.index + const err = args.err + const errTarget = args.errTarget + const lineCheckStr = args.lineCheckStr + const onlyOneChar = args.onlyOneChar === undefined ? false : args.onlyOneChar + activeArgs = { source, index, err, errTarget, onlyOneChar } switch (expectation) { case "always": @@ -116,19 +132,27 @@ export default function (targetWhitespace, expectation, messages) { rejectAfter() break case "always-single-line": - if (!isSingleLineString(lineCheckStr || source)) { return } + if (!isSingleLineString(lineCheckStr || source)) { + return + } expectAfter(messages.expectedAfterSingleLine) break case "never-single-line": - if (!isSingleLineString(lineCheckStr || source)) { return } + if (!isSingleLineString(lineCheckStr || source)) { + return + } rejectAfter(messages.rejectedAfterSingleLine) break case "always-multi-line": - if (isSingleLineString(lineCheckStr || source)) { return } + if (isSingleLineString(lineCheckStr || source)) { + return + } expectAfter(messages.expectedAfterMultiLine) break case "never-multi-line": - if (isSingleLineString(lineCheckStr || source)) { return } + if (isSingleLineString(lineCheckStr || source)) { + return + } rejectAfter(messages.rejectedAfterMultiLine) break default: @@ -137,33 +161,49 @@ export default function (targetWhitespace, expectation, messages) { } function beforeAllowingIndentation(obj) { - before(assign({}, obj, { allowIndentation: true })) + before(Object.assign({}, obj, { allowIndentation: true })) } - function expectBefore(messageFunc = messages.expectedBefore) { + function expectBefore() { + const messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.expectedBefore + if (activeArgs.allowIndentation) { expectBeforeAllowingIndentation(messageFunc) return } - const { source, index } = activeArgs + const _activeArgs = activeArgs + const source = _activeArgs.source, + index = _activeArgs.index + const oneCharBefore = source[index - 1] const twoCharsBefore = source[index - 2] - if (!isValue(oneCharBefore)) { return } + if (!isValue(oneCharBefore)) { + return + } if (targetWhitespace === "space" && oneCharBefore === " ") { - if (activeArgs.onlyOneChar || !isWhitespace(twoCharsBefore)) { return } + if (activeArgs.onlyOneChar || !isWhitespace(twoCharsBefore)) { + return + } } activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])) } - function expectBeforeAllowingIndentation(messageFunc = messages.expectedBefore) { - const { source, index, err } = activeArgs - const expectedChar = (function () { - if (targetWhitespace === "newline") { return "\n" } - }()) + function expectBeforeAllowingIndentation() { + const messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.expectedBefore + const _activeArgs2 = activeArgs + const source = _activeArgs2.source, + index = _activeArgs2.index, + err = _activeArgs2.err + + const expectedChar = function () { + if (targetWhitespace === "newline") { + return "\n" + } + }() let i = index - 1 while (source[i] !== expectedChar) { if (source[i] === "\t" || source[i] === " ") { @@ -175,8 +215,12 @@ export default function (targetWhitespace, expectation, messages) { } } - function rejectBefore(messageFunc = messages.rejectedBefore) { - const { source, index } = activeArgs + function rejectBefore() { + const messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.rejectedBefore + const _activeArgs3 = activeArgs + const source = _activeArgs3.source, + index = _activeArgs3.index + const oneCharBefore = source[index - 1] if (isValue(oneCharBefore) && isWhitespace(oneCharBefore)) { @@ -185,38 +229,53 @@ export default function (targetWhitespace, expectation, messages) { } function afterOneOnly(obj) { - after(assign({}, obj, { onlyOneChar: true })) + after(Object.assign({}, obj, { onlyOneChar: true })) } - function expectAfter(messageFunc = messages.expectedAfter) { - const { source, index } = activeArgs + function expectAfter() { + const messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.expectedAfter + const _activeArgs4 = activeArgs + const source = _activeArgs4.source, + index = _activeArgs4.index const oneCharAfter = source[index + 1] const twoCharsAfter = source[index + 2] - if (!isValue(oneCharAfter)) { return } + if (!isValue(oneCharAfter)) { + return + } if (targetWhitespace === "newline") { // If index is followed by a Windows CR-LF ... if (oneCharAfter === "\r" && twoCharsAfter === "\n") { - if (activeArgs.onlyOneChar || !isWhitespace(source[index + 3])) { return } + if (activeArgs.onlyOneChar || !isWhitespace(source[index + 3])) { + return + } } // If index is followed by a Unix LF ... if (oneCharAfter === "\n") { - if (activeArgs.onlyOneChar || !isWhitespace(twoCharsAfter)) { return } + if (activeArgs.onlyOneChar || !isWhitespace(twoCharsAfter)) { + return + } } } if (targetWhitespace === "space" && oneCharAfter === " ") { - if (activeArgs.onlyOneChar || !isWhitespace(twoCharsAfter)) { return } + if (activeArgs.onlyOneChar || !isWhitespace(twoCharsAfter)) { + return + } } activeArgs.err(messageFunc(activeArgs.errTarget ? activeArgs.errTarget : source[index])) } - function rejectAfter(messageFunc = messages.rejectedAfter) { - const { source, index } = activeArgs + function rejectAfter() { + const messageFunc = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : messages.rejectedAfter + const _activeArgs5 = activeArgs + const source = _activeArgs5.source, + index = _activeArgs5.index + const oneCharAfter = source[index + 1] if (isValue(oneCharAfter) && isWhitespace(oneCharAfter)) { diff --git a/package.json b/package.json index 014ae91414..c627bcfe3b 100644 --- a/package.json +++ b/package.json @@ -19,13 +19,12 @@ "type": "git", "url": "https://github.com/stylelint/stylelint.git" }, - "main": "dist/index.js", - "bin": "dist/cli.js", + "main": "lib/index.js", + "bin": "bin/stylelint", "files": [ "CONTRIBUTING.md", - "dist", "docs", - "src/rules/**/*.md", + "lib", "!**/__tests__" ], "engines": { @@ -68,19 +67,13 @@ "table": "^4.0.1" }, "devDependencies": { - "babel-cli": "^6.1.18", - "babel-eslint": "^7.0.0", - "babel-jest": "^17.0.2", - "babel-plugin-transform-flow-strip-types": "^6.14.0", - "babel-preset-es2015": "^6.14.0", - "babel-preset-es2017": "^6.14.0", - "babel-tape-runner": "^2.0.0", "benchmark": "^2.1.0", + "cli-cursor": "^1.0.2", "common-tags": "^1.3.0", "coveralls": "^2.11.9", + "d3-queue": "^3.0.3", "eslint": "~3.11.0", "eslint-config-stylelint": "^5.0.0", - "eslint-plugin-flowtype": "^2.21.0", "flow-bin": "^0.36.0", "jest": "^17.0.3", "npm-run-all": "^3.0.0", @@ -93,41 +86,31 @@ "remark-preset-lint-recommended": "^1.0.0", "remark-validate-links": "^5.0.0", "request": "^2.69.0", - "rimraf": "^2.5.2", "sinon": "^1.16.1", "strip-ansi": "^3.0.1", + "tap-out": "^1.4.2", + "tap-parser": "^3.0.4", "tape": "^4.2.0" }, "scripts": { "jest": "jest", - "jest-src": "npm run jest -- --testPathPattern /src/", + "jest-ci": "jest -i", + "jest-src": "npm run jest -- --testPathPattern /lib/", "jest-system": "npm run jest -- --testPathPattern /system-tests/", - "benchmark-rule": "babel-node scripts/benchmark-rule.js", + "benchmark-rule": "node scripts/benchmark-rule.js", "coveralls": "nyc report --reporter=text-lcov | coveralls", "lint:js": "eslint .", "lint:md": "remark . --quiet --frail", "lint": "npm-run-all --parallel lint:*", "flow": "flow", - "tape": "babel-tape-runner \"src/**/__tests__/**/*-test.js\" \"src/rules/**/__tests__/*.js\"", + "tape": "node scripts/rules-tape.js", "test": "nyc npm run tape", - "prebuild": "rimraf dist", - "build": "babel src --out-dir dist", - "prepublish": "npm run build", "release": "npmpub" }, - "babel": { - "presets": [ - "es2015", - "es2017" - ], - "plugins": [ - "transform-flow-strip-types" - ] - }, "jest": { "testEnvironment": "node", "testPathDirs": [ - "src", + "lib", "system-tests" ], "testRegex": ".*\\.test\\.js$" diff --git a/scripts/benchmark-rule.js b/scripts/benchmark-rule.js index 3cb6465816..d3458c03f6 100644 --- a/scripts/benchmark-rule.js +++ b/scripts/benchmark-rule.js @@ -1,10 +1,12 @@ +"use strict" + /* eslint-disable no-console */ -import Benchmark from "benchmark" -import chalk from "chalk" -import normalizeRuleSettings from "../src/normalizeRuleSettings" -import postcss from "postcss" -import request from "request" -import rules from "../src/rules" +const Benchmark = require("benchmark") +const chalk = require("chalk") +const normalizeRuleSettings = require("../lib/normalizeRuleSettings") +const postcss = require("postcss") +const request = require("request") +const rules = require("../lib/rules") const ruleName = process.argv[2] const ruleOptions = process.argv[3] diff --git a/scripts/rules-tape.js b/scripts/rules-tape.js new file mode 100755 index 0000000000..3374f26a4f --- /dev/null +++ b/scripts/rules-tape.js @@ -0,0 +1,126 @@ +#!/usr/bin/env node +"use strict" +/* eslint-disable no-console */ + +const queue = require("d3-queue").queue +const path = require("path") +const chalk = require("chalk") +const globby = require("globby") +const child_process = require("child_process") +const tapParser = require("tap-out") +const cliCursor = require("cli-cursor") + +const ruleNames = process.argv.slice(2) +const fileGlobs = (ruleNames.length > 0) + ? ruleNames.map((ruleName) => path.join(__dirname, `../lib/rules/${ruleName}/__tests__/*.js`)) + : path.join(__dirname, "../lib/rules/**/__tests__/*.js") + +// These numbers were arrived at by guess-and-check, +// seeing which combo seemed to produce the fastest +// results +const CHUNK_SIZE = 5 +const CHUNK_CONCURRENCY = 10 + +const q = queue(CHUNK_CONCURRENCY) + +cliCursor.hide() +console.log(chalk.bold.dim("Testing ...\n")) + +globby(fileGlobs).then((paths) => { + let exitCode = 0 + + let totalTests = 0 + let totalPasses = 0 + let totalFails = 0 + const fails = [] + + let dots + let dotPlace = 0 + const updateDots = () => { + dotPlace = (dotPlace + 1 > 4) ? 0 : dotPlace + 1 + if (dotPlace === 4) dots = chalk.cyan("....") + if (dotPlace === 3) dots = chalk.cyan("...") + chalk.dim(".") + if (dotPlace === 2) dots = chalk.cyan("..") + chalk.dim("..") + if (dotPlace === 1) dots = chalk.cyan(".") + chalk.dim("...") + if (dotPlace === 0) dots = chalk.dim("....") + dots = chalk.cyan(dots) + } + updateDots() + const dotInterval = setInterval(updateDots, 300) + + const chunkCount = Math.ceil(paths.length / CHUNK_SIZE) + + for (let i = 0; i < chunkCount; i++) { + const chunkPaths = paths.slice(i * CHUNK_SIZE, (i + 1) * CHUNK_SIZE) + + const parser = tapParser() + const comments = {} + const failures = [] + parser.on("assert", (assertion) => { + totalTests += 1 + if (!process.env.CI) { + process.stdout.clearLine() + process.stdout.cursorTo(0) + process.stdout.write(`${dots} ${totalTests}`) + } + if (assertion.ok) { + totalPasses += 1 + } else { + totalFails += 1 + } + }) + parser.on("comment", (comment) => { + if (comments[comment.test]) { + comments[comment.test] += `\n${comment.raw}` + } else { + comments[comment.test] = comment.raw + } + }) + parser.on("fail", (failure) => { + failures.push(failure) + }) + parser.on("error", (err) => { + console.error(err) + process.exit(1) + }) + + q.defer((done) => { + const tapeProcess = child_process.spawn("tape", chunkPaths, { shell: true }) + tapeProcess.on("error", done) + tapeProcess.stdout.pipe(parser) + tapeProcess.on("close", (code) => { + failures.forEach((failure) => { + fails.push({ + assertion: failure, + comment: comments[failure.test], + }) + }) + if (code !== 0) exitCode = code + done() + }) + }) + } + q.awaitAll((err) => { + clearInterval(dotInterval) + if (err) return console.error(err.stack) + if (!process.env.CI) { + process.stdout.clearLine() + process.stdout.cursorTo(0) + } + console.log(chalk.green(`ok: ${totalPasses}`)) + console.log(chalk.red(`not ok: ${totalFails}`)) + console.log(chalk.bold(`total: ${totalTests}`)) + + if (fails.length > 0) { + console.log(chalk.red.underline("\nfailures")) + fails.forEach((fail) => { + console.log(`\n${fail.comment}`) + console.log(` ${fail.assertion.name}`) + console.log(chalk.green(` expected: ${fail.assertion.error.expected}`)) + console.log(chalk.red(` actual: ${fail.assertion.error.actual}`)) + }) + } + + process.exit(exitCode) + }) +}) diff --git a/scripts/visual.sh b/scripts/visual.sh index 6a2920443d..403bdec4c4 100755 --- a/scripts/visual.sh +++ b/scripts/visual.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash echo "Normal:" -node ../dist/cli.js visual.css --config=visual-config.json +node ../bin/stylelint.js visual.css --config=visual-config.json echo -e "\n\nVerbose:" -node ../dist/cli.js visual.css --config=visual-config.json --formatter verbose +node ../bin/stylelint.js visual.css --config=visual-config.json --formatter verbose diff --git a/src/__tests__/fixtures/plugin-array.js b/src/__tests__/fixtures/plugin-array.js deleted file mode 100644 index 3e5d00c1c4..0000000000 --- a/src/__tests__/fixtures/plugin-array.js +++ /dev/null @@ -1,7 +0,0 @@ -import conditionallyCheckColorHexCase from "./plugin-conditionally-check-color-hex-case" -import warnAboutFoo from "./plugin-warn-about-foo" - -export default [ - conditionallyCheckColorHexCase, - warnAboutFoo, -] diff --git a/src/__tests__/message.test.js b/src/__tests__/message.test.js deleted file mode 100644 index 65966cfe7e..0000000000 --- a/src/__tests__/message.test.js +++ /dev/null @@ -1,12 +0,0 @@ -import path from "path" -import standalone from "../standalone" - -it("standalone loading YAML with custom message", () => { - return standalone({ - code: "a { color: pink; }", - configFile: path.join(__dirname, "fixtures/config-color-named-custom-message.yaml"), - }).then(({ results }) => { - expect(results[0].warnings.length).toBe(1) - expect(results[0].warnings[0].text).toBe("Unacceptable") - }) -}) diff --git a/src/__tests__/needlessDisables.test.js b/src/__tests__/needlessDisables.test.js deleted file mode 100644 index c6b60bc277..0000000000 --- a/src/__tests__/needlessDisables.test.js +++ /dev/null @@ -1,110 +0,0 @@ -import needlessDisables from "../needlessDisables" -import path from "path" -import standalone from "../standalone" -import { stripIndent } from "common-tags" - -function fixture(name) { - return path.join(__dirname, "./fixtures/needlessDisables", name) -} - -it("needlessDisables simple case", () => { - const config = { - rules: { "block-no-empty": true }, - } - - const css = stripIndent` - /* stylelint-disable */ - a {} - /* stylelint-enable */ - a { - b {} /* stylelint-disable-line block-no-empty */ - } - /* stylelint-disable */ - a { color: pink; } - /* stylelint-enable */ - a { - b { color: pink; } /* stylelint-disable-line block-no-empty */ - } - ` - - return standalone({ - config, - code: css, - ignoreDisables: true, - }).then(({ results }) => { - const report = needlessDisables(results) - expect(report.length).toBe(1) - expect(report[0].ranges).toEqual([ - { start: 7, end: 9 }, - { start: 11, end: 11 }, - ]) - }) -}) - -it("needlessDisables complex case", () => { - const config = { - rules: { - "block-no-empty": true, - "color-named": "never", - }, - } - - return standalone({ - config, - files: [ - fixture("disabled-ranges-1.css"), - fixture("disabled-ranges-2.css"), - // ignore files contain `CssSyntaxError` - fixture("disabled-ranges-3.css"), - ], - ignoreDisables: true, - }).then(({ results }) => { - expect(needlessDisables(results)).toEqual([ - { - source: fixture("disabled-ranges-1.css"), - ranges: [ - { start: 1, end: 3 }, - { start: 5, end: 7 }, - { start: 10, end: 10 }, - ], - }, - { - source: fixture("disabled-ranges-2.css"), - ranges: [ - { start: 5, end: 5 }, - { start: 6, end: 6 }, - { start: 8 }, - ], - }, - ]) - }) -}) - -it("needlessDisables ignored case", () => { - const config = { - rules: { - "block-no-empty": true, - }, - } - - return standalone({ - config, - files: [ - fixture("disabled-ranges-1.css"), - fixture("ignored-file.css"), - ], - ignoreDisables: true, - ignorePath: fixture(".stylelintignore"), - }).then(({ results }) => { - expect(needlessDisables(results)).toEqual([ - { - source: fixture("disabled-ranges-1.css"), - ranges: [ - { start: 1, end: 3 }, - { start: 5, end: 7 }, - { start: 10, end: 10 }, - ], - }, - ]) - }) -}) diff --git a/src/createPlugin.js b/src/createPlugin.js deleted file mode 100644 index bde6f52ac4..0000000000 --- a/src/createPlugin.js +++ /dev/null @@ -1,6 +0,0 @@ -export default function (ruleName, rule) { - return { - ruleName, - rule, - } -} diff --git a/src/formatters/__tests__/needlessDisablesStringFormatter-test.js b/src/formatters/__tests__/needlessDisablesStringFormatter-test.js deleted file mode 100644 index c7283c9cae..0000000000 --- a/src/formatters/__tests__/needlessDisablesStringFormatter-test.js +++ /dev/null @@ -1,40 +0,0 @@ -import chalk from "chalk" -import needlessDisablesStringFormatter from "../needlessDisablesStringFormatter" -import { stripIndent } from "common-tags" -import test from "tape" - -test("needlessDisables formatter stringified", t => { - const actual = chalk.stripColor(needlessDisablesStringFormatter([ - { - source: "foo", - ranges: [ - { start: 1, end: 3 }, - { start: 7 }, - ], - }, - { - source: "bar", - ranges: [ - { start: 19, end: 33 }, - { start: 99, end: 102 }, - ], - }, - { - sourc: "baz", - ranges: [], - }, - ])) - - let expected = stripIndent` - foo - start: 1, end: 3 - start: 7 - - bar - start: 19, end: 33 - start: 99, end: 102` - expected = `\n${expected}\n` - - t.equal(actual, expected) - t.end() -}) diff --git a/src/formatters/index.js b/src/formatters/index.js deleted file mode 100644 index dfbae83597..0000000000 --- a/src/formatters/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { default as json } from "./jsonFormatter" -export { default as string } from "./stringFormatter" -export { default as verbose } from "./verboseFormatter" diff --git a/src/getConfigForFile.js b/src/getConfigForFile.js deleted file mode 100644 index b318423506..0000000000 --- a/src/getConfigForFile.js +++ /dev/null @@ -1,47 +0,0 @@ -/* @flow */ -import { augmentConfigFull } from "./augmentConfig" -import { configurationError } from "./utils" -import path from "path" - -export default function ( - stylelint: stylelint$internalApi, - searchPath?: string, -): Promise { - searchPath = searchPath || process.cwd() - - const optionsConfig = stylelint._options.config - - if (optionsConfig !== undefined) { - const cached = stylelint._specifiedConfigCache.get(optionsConfig) - if (cached) return cached - - // stylelint._fullExplorer (cosmiconfig) is already configured to - // run augmentConfigFull; but since we're making up the result here, - // we need to manually run the transform - const augmentedResult = augmentConfigFull(stylelint, { - config: optionsConfig, - // Add the extra path part so that we can get the directory without being - // confused - filepath: path.join(process.cwd(), "argument-config"), - }) - stylelint._specifiedConfigCache.set(optionsConfig, augmentedResult) - return augmentedResult - } - - return stylelint._fullExplorer.load(searchPath, stylelint._options.configFile) - .then((config) => { - // If no config was found, try looking from process.cwd - if (!config) return stylelint._fullExplorer.load(process.cwd()) - return config - }) - .then((config) => { - if (!config) { - const ending = (searchPath) ? ` for ${searchPath}` : "" - throw configurationError(`No configuration provided${ending}`) - } - return config - }) -} diff --git a/src/getPostcssResult.js b/src/getPostcssResult.js deleted file mode 100644 index 4183ff28c2..0000000000 --- a/src/getPostcssResult.js +++ /dev/null @@ -1,98 +0,0 @@ -/* @flow */ -import fs from "fs" -import lessSyntax from "postcss-less" -import path from "path" -import postcss from "postcss" -import scssSyntax from "postcss-scss" -import sugarssSyntax from "sugarss" - -const postcssProcessor = postcss() - -export default function ( - stylelint: stylelint$internalApi, - options: { - code?: string, - codeFilename?: string, - filePath?: string, - codeProcessors?: Array, - syntax?: stylelint$syntaxes, - customSyntax?: string - } = {} -): Promise { - const cached = stylelint._postcssResultCache.get(options.filePath) - if (cached) return Promise.resolve(cached) - - let getCode - if (options.code !== undefined) { - getCode = Promise.resolve(options.code) - } else if (options.filePath) { - getCode = readFile(options.filePath) - } - - if (!getCode) { - throw new Error("code or filePath required") - } - - return getCode - .then((code) => { - const { customSyntax } = stylelint._options - let { syntax } = stylelint._options - if (customSyntax) { - try { - syntax = require(customSyntax) - } catch (e) { - throw new Error(`Cannot resolve custom syntax module ${customSyntax}`) - } - } else { - const fileExtension = path.extname(options.filePath || "") - if (syntax === "scss" || !syntax && fileExtension === ".scss") { - syntax = scssSyntax - } else if (syntax === "less" || !syntax && fileExtension === ".less") { - syntax = lessSyntax - } else if (syntax === "sugarss" || !syntax && fileExtension === ".sss") { - syntax = sugarssSyntax - } else if (syntax) { - throw new Error("You must use a valid syntax option, either: scss, less or sugarss") - } - } - - const postcssOptions = {} - - postcssOptions.from = options.filePath - - /* - * PostCSS allows for syntaxes that only contain a parser, however, - * it then expects the syntax to be set as the `parser` option rather than `syntax. - */ - if (syntax && !syntax.stringify) { - postcssOptions.parser = syntax - } else { - postcssOptions.syntax = syntax - } - - const source = (options.code) - ? options.codeFilename - : options.filePath - let preProcessedCode = code - if (options.codeProcessors) { - options.codeProcessors.forEach((codeProcessor) => { - preProcessedCode = codeProcessor(preProcessedCode, source) - }) - } - - return postcssProcessor.process(preProcessedCode, postcssOptions) - }) - .then((postcssResult) => { - stylelint._postcssResultCache.set(options.filePath, postcssResult) - return postcssResult - }) -} - -function readFile(filePath: string): Promise { - return new Promise((resolve, reject) => { - fs.readFile(filePath, "utf8", (err, content) => { - if (err) { return reject(err) } - resolve(content) - }) - }) -} diff --git a/src/index.js b/src/index.js deleted file mode 100644 index 5d39120379..0000000000 --- a/src/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "./utils" -import createPlugin from "./createPlugin" -import createRuleTester from "./testUtils/createRuleTester" -import createStylelint from "./createStylelint" -import postcssPlugin from "./postcssPlugin" -import rules from "./rules" -import standalone from "./standalone" - -const api = postcssPlugin - -api.utils = { - report, - ruleMessages, - validateOptions, -} - -api.lint = standalone -api.rules = rules -api.createPlugin = createPlugin -api.createRuleTester = createRuleTester -api.createLinter = createStylelint - -module.exports = api diff --git a/src/postcssPlugin.js b/src/postcssPlugin.js deleted file mode 100644 index 272681164e..0000000000 --- a/src/postcssPlugin.js +++ /dev/null @@ -1,18 +0,0 @@ -/* @flow */ -import _ from "lodash" -import createStylelint from "./createStylelint" -import postcss from "postcss" - -export default postcss.plugin("stylelint", (options = {}) => { - const tailoredOptions: Object = (options.rules) - ? { config: options } - : options - - const stylelint = createStylelint(tailoredOptions) - return (root, result) => { - return stylelint._lintSource({ - filePath: options.from || _.get(root, "source.input.file"), - existingPostcssResult: result, - }) - } -}) diff --git a/src/reference/keywordSets.js b/src/reference/keywordSets.js deleted file mode 100644 index 04bc8d05cf..0000000000 --- a/src/reference/keywordSets.js +++ /dev/null @@ -1,598 +0,0 @@ -import _ from "lodash" - -export const nonLengthUnits = new Set([ - // Relative length units - "%", - // Time length units - "s", - "ms", - // Angle - "deg", - "grad", - "turn", - "rad", - // Frequency - "Hz", - "kHz", - // Resolution - "dpi", - "dpcm", - "dppx", -]) - -export const lengthUnits = new Set([ - // Relative length units - "em", - "ex", - "ch", - "rem", - // Viewport-percentage lengths - "vh", - "vw", - "vmin", - "vmax", - "vm", - // Absolute length units - "px", - "mm", - "cm", - "in", - "pt", - "pc", - "q", -]) - -export const units = uniteSets( - nonLengthUnits, - lengthUnits -) - -export const colorFunctionNames = new Set([ - "rgb", - "rgba", - "hsl", - "hsla", - "hwb", - "gray", -]) - -export const camelCaseFunctionNames = new Set([ - "translateX", - "translateY", - "translateZ", - "scaleX", - "scaleY", - "scaleZ", - "rotateX", - "rotateY", - "rotateZ", - "skewX", - "skewY", -]) - -export const basicKeywords = new Set([ - "initial", - "inherit", - "unset", -]) - -export const fontFamilyKeywords = uniteSets(basicKeywords, [ - "serif", - "sans-serif", - "cursive", - "fantasy", - "monospace", -]) - -export const fontWeightRelativeKeywords = new Set([ - "bolder", - "lighter", -]) - -export const fontWeightAbsoluteKeywords = new Set([ - "bold", -]) - -export const fontWeightNumericKeywords = new Set([ - "100", - "200", - "300", - "400", - "500", - "600", - "700", - "800", - "900", -]) - -export const fontWeightKeywords = uniteSets( - basicKeywords, - fontWeightRelativeKeywords, - fontWeightAbsoluteKeywords, - fontWeightNumericKeywords -) - -export const animationNameKeywords = uniteSets(basicKeywords, [ - "none", -]) - -export const animationTimingFunctionKeywords = uniteSets(basicKeywords, [ - "linear", - "ease", - "ease-in", - "ease-in-out", - "ease-out", - "step-start", - "step-end", - "steps", - "cubic-bezier", -]) - -export const animationIterationCountKeywords = new Set([ - "infinite", -]) - -export const animationDirectionKeywords = uniteSets(basicKeywords, [ - "normal", - "reverse", - "alternate", - "alternate-reverse", -]) - -export const animationFillModeKeywords = new Set([ - "none", - "forwards", - "backwards", - "both", -]) - -export const animationPlayStateKeywords = uniteSets(basicKeywords, [ - "running", - "paused", -]) - -// cf. https://developer.mozilla.org/en-US/docs/Web/CSS/animation -export const animationShorthandKeywords = uniteSets( - basicKeywords, - animationNameKeywords, - animationTimingFunctionKeywords, - animationIterationCountKeywords, - animationDirectionKeywords, - animationFillModeKeywords, - animationPlayStateKeywords -) - -// These are the ones that can have single-colon notation -export const levelOneAndTwoPseudoElements = new Set([ - "before", - "after", - "first-line", - "first-letter", -]) - -// These are the ones that require double-colon notation -export const levelThreePseudoElements = new Set([ - "before", - "after", - "first-line", - "first-letter", - "selection", - "spelling-error", - "grammar-error", - "backdrop", - "marker", - "placeholder", - "shadow", - "slotted", - "content", -]) - -export const pseudoElements = uniteSets( - levelOneAndTwoPseudoElements, - levelThreePseudoElements -) - -export const aNPlusBNotationPseudoClasses = new Set([ - "nth-child", - "nth-column", - "nth-last-child", - "nth-last-column", - "nth-last-of-type", - "nth-of-type", -]) - -export const linguisticPseudoClasses = new Set([ - "dir", - "lang", -]) - -export const otherPseudoClasses = new Set([ - "active", - "any-link", - "blank", - "checked", - "contains", - "current", - "default", - "disabled", - "drop", - "empty", - "enabled", - "first-child", - "first-of-type", - "focus", - "focus-within", - "fullscreen", - "future", - "has", - "host", - "host-context", - "hover", - "indeterminate", - "in-range", - "invalid", - "last-child", - "last-of-type", - "link", - "matches", - "not", - "only-child", - "only-of-type", - "optional", - "out-of-range", - "past", - "placeholder-shown", - "read-only", - "read-write", - "required", - "root", - "scope", - "target", - "user-error", - "user-invalid", - "val", - "valid", - "visited", -]) - -export const pseudoClasses = uniteSets( - aNPlusBNotationPseudoClasses, - linguisticPseudoClasses, - otherPseudoClasses -) - -export const shorthandTimeProperties = new Set([ - "transition", - "animation", -]) - -export const longhandTimeProperties = new Set([ - "transition-duration", - "transition-delay", - "animation-duration", - "animation-delay", -]) - -export const timeProperties = uniteSets( - shorthandTimeProperties, - longhandTimeProperties -) - -export const camelCaseKeywords = new Set([ - "optimizeSpeed", - "optimizeQuality", - "optimizeLegibility", - "geometricPrecision", - "currentColor", - "crispEdges", - "visiblePainted", - "visibleFill", - "visibleStroke", - "sRGB", - "linearRGB", -]) - -// https://developer.mozilla.org/docs/Web/CSS/counter-increment -export const counterIncrementKeywords = uniteSets(basicKeywords, [ - "none", -]) - -export const gridRowKeywords = uniteSets(basicKeywords, [ - "auto", - "span", -]) - -export const gridColumnKeywords = uniteSets(basicKeywords, [ - "auto", - "span", -]) - -export const gridAreaKeywords = uniteSets(basicKeywords, [ - "auto", - "span", -]) - -// https://developer.mozilla.org/ru/docs/Web/CSS/list-style-type -export const listStyleTypeKeywords = uniteSets(basicKeywords, [ - "none", - "disc", - "circle", - "square", - "decimal", - "cjk-decimal", - "decimal-leading-zero", - "lower-roman", - "upper-roman", - "lower-greek", - "lower-alpha", - "lower-latin", - "upper-alpha", - "upper-latin", - "arabic-indic", - "armenian", - "bengali", - "cambodian", - "cjk-earthly-branch", - "cjk-ideographic", - "devanagari", - "ethiopic-numeric", - "georgian", - "gujarati", - "gurmukhi", - "hebrew", - "hiragana", - "hiragana-iroha", - "japanese-formal", - "japanese-informal", - "kannada", - "katakana", - "katakana-iroha", - "khmer", - "korean-hangul-formal", - "korean-hanja-formal", - "korean-hanja-informal", - "lao", - "lower-armenian", - "malayalam", - "mongolian", - "myanmar", - "oriya", - "persian", - "simp-chinese-formal", - "simp-chinese-informal", - "tamil", - "telugu", - "thai", - "tibetan", - "trad-chinese-formal", - "trad-chinese-informal", - "upper-armenian", - "disclosure-open", - "disclosure-closed", - // Non-standard extensions (without prefixe) - "ethiopic-halehame", - "ethiopic-halehame-am", - "ethiopic-halehame-ti-er", - "ethiopic-halehame-ti-et", - "hangul", - "hangul-consonant", - "urdu", -]) - -export const listStylePositionKeywords = uniteSets(basicKeywords, [ - "inside", - "outside", -]) - -export const listStyleImageKeywords = uniteSets(basicKeywords, [ - "none", -]) - -export const listStyleShorthandKeywords = uniteSets( - basicKeywords, - listStyleTypeKeywords, - listStylePositionKeywords, - listStyleImageKeywords -) - -export const fontStyleKeywords = uniteSets(basicKeywords, [ - "normal", - "italic", - "oblique", -]) - -export const fontVariantKeywords = uniteSets(basicKeywords, [ - "normal", - "none", - "historical-forms", - "none", - "common-ligatures", - "no-common-ligatures", - "discretionary-ligatures", - "no-discretionary-ligatures", - "historical-ligatures", - "no-historical-ligatures", - "contextual", - "no-contextual", - "small-caps", - "small-caps", - "all-small-caps", - "petite-caps", - "all-petite-caps", - "unicase", - "titling-caps", - "lining-nums", - "oldstyle-nums", - "proportional-nums", - "tabular-nums", - "diagonal-fractions", - "stacked-fractions", - "ordinal", - "slashed-zero", - "jis78", - "jis83", - "jis90", - "jis04", - "simplified", - "traditional", - "full-width", - "proportional-width", - "ruby", -]) - -export const fontStretchKeywords = uniteSets(basicKeywords, [ - "semi-condensed", - "condensed", - "extra-condensed", - "ultra-condensed", - "semi-expanded", - "expanded", - "extra-expanded", - "ultra-expanded", -]) - -export const fontSizeKeywords = uniteSets(basicKeywords, [ - "xx-small", - "x-small", - "small", - "medium", - "large", - "x-large", - "xx-large", - "larger", - "smaller", -]) - -export const lineHeightKeywords = uniteSets(basicKeywords, [ - "normal", -]) - -export const fontShorthandKeywords = uniteSets( - basicKeywords, - fontStyleKeywords, - fontVariantKeywords, - fontWeightKeywords, - fontStretchKeywords, - fontSizeKeywords, - lineHeightKeywords, - fontFamilyKeywords -) - -export const keyframeSelectorKeywords = new Set([ - "from", - "to", -]) - -// https://developer.mozilla.org/en/docs/Web/CSS/At-rule -export const atRules = new Set([ - "apply", - "annotation", - "character-variant", - "charset", - "counter-style", - "custom-media", - "custom-selector", - "document", - "font-face", - "font-feature-values", - "import", - "keyframes", - "media", - "namespace", - "nest", - "ornaments", - "page", - "styleset", - "stylistic", - "supports", - "swash", - "viewport", -]) - -// https://drafts.csswg.org/mediaqueries/#descdef-media-update -export const deprecatedMediaFeatureNames = new Set([ - "device-aspect-ratio", - "device-height", - "device-width", - "max-device-aspect-ratio", - "max-device-height", - "max-device-width", - "min-device-aspect-ratio", - "min-device-height", - "min-device-width", -]) - -// https://drafts.csswg.org/mediaqueries/#descdef-media-update -export const mediaFeatureNames = uniteSets(deprecatedMediaFeatureNames, [ - "any-hover", - "any-pointer", - "aspect-ratio", - "color", - "color-gamut", - "color-index", - "grid", - "height", - "hover", - "max-aspect-ratio", - "max-color", - "max-color-index", - "max-height", - "max-monochrome", - "max-resolution", - "max-width", - "min-aspect-ratio", - "min-color", - "min-color-index", - "min-height", - "min-monochrome", - "min-resolution", - "min-width", - "monochrome", - "orientation", - "overflow-block", - "overflow-inline", - "pointer", - "resolution", - "scan", - "scripting", - "update", - "width", -]) - -// https://www.w3.org/TR/CSS22/ui.html#system-colors -export const systemColors = new Set([ - "activeborder", - "activecaption", - "appworkspace", - "background", - "buttonface", - "buttonhighlight", - "buttonshadow", - "buttontext", - "captiontext", - "graytext", - "highlight", - "highlighttext", - "inactiveborder", - "inactivecaption", - "inactivecaptiontext", - "infobackground", - "infotext", - "menu", - "menutext", - "scrollbar", - "threeddarkshadow", - "threedface", - "threedhighlight", - "threedlightshadow", - "threedshadow", - "window", - "windowframe", - "windowtext", -]) - -function uniteSets(...sets) { - return new Set(sets.reduce((result, set) => { - return result.concat(_.toArray(set)) - }, [])) -} diff --git a/src/reference/namedColorData.js b/src/reference/namedColorData.js deleted file mode 100644 index 1254f6542a..0000000000 --- a/src/reference/namedColorData.js +++ /dev/null @@ -1,3030 +0,0 @@ -export default { - "aliceblue": { - "hex": [ - "#f0f8ff", - "#fff0f8ff", - ], - "func": [ - "rgb(240,248,255)", - "rgba(240,248,255,1)", - "rgba(240,248,255,100%)", - "rgb(94%,97%,100%)", - "rgba(94%,97%,100%,1)", - "rgba(94%,97%,100%,100%)", - "hsl(208,100%,97%)", - "hsla(208,100%,97%,1)", - "hsla(208,100%,97%,100%)", - "hwb(208,94%,0%)", - "hwb(208,94%,0%,1)", - "hwb(208,94%,0%,100%)", - ], - }, - "antiquewhite": { - "hex": [ - "#faebd7", - "#fffaebd7", - ], - "func": [ - "rgb(250,235,215)", - "rgba(250,235,215,1)", - "rgba(250,235,215,100%)", - "rgb(98%,92%,84%)", - "rgba(98%,92%,84%,1)", - "rgba(98%,92%,84%,100%)", - "hsl(34,78%,91%)", - "hsla(34,78%,91%,1)", - "hsla(34,78%,91%,100%)", - "hwb(34,84%,2%)", - "hwb(34,84%,2%,1)", - "hwb(34,84%,2%,100%)", - ], - }, - "aqua": { - "hex": [ - "#00ffff", - "#ff00ffff", - "#0ff", - "#f0ff", - ], - "func": [ - "rgb(0,255,255)", - "rgba(0,255,255,1)", - "rgba(0,255,255,100%)", - "rgb(0%,100%,100%)", - "rgba(0%,100%,100%,1)", - "rgba(0%,100%,100%,100%)", - "hsl(180,100%,50%)", - "hsla(180,100%,50%,1)", - "hsla(180,100%,50%,100%)", - "hwb(180,0%,0%)", - "hwb(180,0%,0%,1)", - "hwb(180,0%,0%,100%)", - ], - }, - "aquamarine": { - "hex": [ - "#7fffd4", - "#ff7fffd4", - ], - "func": [ - "rgb(127,255,212)", - "rgba(127,255,212,1)", - "rgba(127,255,212,100%)", - "rgb(50%,100%,83%)", - "rgba(50%,100%,83%,1)", - "rgba(50%,100%,83%,100%)", - "hsl(160,100%,75%)", - "hsla(160,100%,75%,1)", - "hsla(160,100%,75%,100%)", - "hwb(160,50%,0%)", - "hwb(160,50%,0%,1)", - "hwb(160,50%,0%,100%)", - ], - }, - "azure": { - "hex": [ - "#f0ffff", - "#fff0ffff", - ], - "func": [ - "rgb(240,255,255)", - "rgba(240,255,255,1)", - "rgba(240,255,255,100%)", - "rgb(94%,100%,100%)", - "rgba(94%,100%,100%,1)", - "rgba(94%,100%,100%,100%)", - "hsl(180,100%,97%)", - "hsla(180,100%,97%,1)", - "hsla(180,100%,97%,100%)", - "hwb(180,94%,0%)", - "hwb(180,94%,0%,1)", - "hwb(180,94%,0%,100%)", - ], - }, - "beige": { - "hex": [ - "#f5f5dc", - "#fff5f5dc", - ], - "func": [ - "rgb(245,245,220)", - "rgba(245,245,220,1)", - "rgba(245,245,220,100%)", - "rgb(96%,96%,86%)", - "rgba(96%,96%,86%,1)", - "rgba(96%,96%,86%,100%)", - "hsl(60,56%,91%)", - "hsla(60,56%,91%,1)", - "hsla(60,56%,91%,100%)", - "hwb(60,86%,4%)", - "hwb(60,86%,4%,1)", - "hwb(60,86%,4%,100%)", - ], - }, - "bisque": { - "hex": [ - "#ffe4c4", - "#ffffe4c4", - ], - "func": [ - "rgb(255,228,196)", - "rgba(255,228,196,1)", - "rgba(255,228,196,100%)", - "rgb(100%,89%,77%)", - "rgba(100%,89%,77%,1)", - "rgba(100%,89%,77%,100%)", - "hsl(33,100%,88%)", - "hsla(33,100%,88%,1)", - "hsla(33,100%,88%,100%)", - "hwb(33,77%,0%)", - "hwb(33,77%,0%,1)", - "hwb(33,77%,0%,100%)", - ], - }, - "black": { - "hex": [ - "#000000", - "#ff000000", - "#000", - "#f000", - ], - "func": [ - "rgb(0,0,0)", - "rgba(0,0,0,1)", - "rgba(0,0,0,100%)", - "rgb(0%,0%,0%)", - "rgba(0%,0%,0%,1)", - "rgba(0%,0%,0%,100%)", - "hsl(0,0%,0%)", - "hsla(0,0%,0%,1)", - "hsla(0,0%,0%,100%)", - "hwb(0,0%,100%)", - "hwb(0,0%,100%,1)", - "hwb(0,0%,100%,100%)", - "gray(0)", - "gray(0,1)", - "gray(0,100%)", - "gray(0%)", - "gray(0%,1)", - "gray(0%,100%)", - ], - }, - "blanchedalmond": { - "hex": [ - "#ffebcd", - "#ffffebcd", - ], - "func": [ - "rgb(255,235,205)", - "rgba(255,235,205,1)", - "rgba(255,235,205,100%)", - "rgb(100%,92%,80%)", - "rgba(100%,92%,80%,1)", - "rgba(100%,92%,80%,100%)", - "hsl(36,100%,90%)", - "hsla(36,100%,90%,1)", - "hsla(36,100%,90%,100%)", - "hwb(36,80%,0%)", - "hwb(36,80%,0%,1)", - "hwb(36,80%,0%,100%)", - ], - }, - "blue": { - "hex": [ - "#0000ff", - "#ff0000ff", - "#00f", - "#f00f", - ], - "func": [ - "rgb(0,0,255)", - "rgba(0,0,255,1)", - "rgba(0,0,255,100%)", - "rgb(0%,0%,100%)", - "rgba(0%,0%,100%,1)", - "rgba(0%,0%,100%,100%)", - "hsl(240,100%,50%)", - "hsla(240,100%,50%,1)", - "hsla(240,100%,50%,100%)", - "hwb(240,0%,0%)", - "hwb(240,0%,0%,1)", - "hwb(240,0%,0%,100%)", - ], - }, - "blueviolet": { - "hex": [ - "#8a2be2", - "#ff8a2be2", - ], - "func": [ - "rgb(138,43,226)", - "rgba(138,43,226,1)", - "rgba(138,43,226,100%)", - "rgb(54%,17%,89%)", - "rgba(54%,17%,89%,1)", - "rgba(54%,17%,89%,100%)", - "hsl(271,76%,53%)", - "hsla(271,76%,53%,1)", - "hsla(271,76%,53%,100%)", - "hwb(271,17%,11%)", - "hwb(271,17%,11%,1)", - "hwb(271,17%,11%,100%)", - ], - }, - "brown": { - "hex": [ - "#a52a2a", - "#ffa52a2a", - ], - "func": [ - "rgb(165,42,42)", - "rgba(165,42,42,1)", - "rgba(165,42,42,100%)", - "rgb(65%,16%,16%)", - "rgba(65%,16%,16%,1)", - "rgba(65%,16%,16%,100%)", - "hsl(0,59%,41%)", - "hsla(0,59%,41%,1)", - "hsla(0,59%,41%,100%)", - "hwb(0,16%,35%)", - "hwb(0,16%,35%,1)", - "hwb(0,16%,35%,100%)", - ], - }, - "burlywood": { - "hex": [ - "#deb887", - "#ffdeb887", - ], - "func": [ - "rgb(222,184,135)", - "rgba(222,184,135,1)", - "rgba(222,184,135,100%)", - "rgb(87%,72%,53%)", - "rgba(87%,72%,53%,1)", - "rgba(87%,72%,53%,100%)", - "hsl(34,57%,70%)", - "hsla(34,57%,70%,1)", - "hsla(34,57%,70%,100%)", - "hwb(34,53%,13%)", - "hwb(34,53%,13%,1)", - "hwb(34,53%,13%,100%)", - ], - }, - "cadetblue": { - "hex": [ - "#5f9ea0", - "#ff5f9ea0", - ], - "func": [ - "rgb(95,158,160)", - "rgba(95,158,160,1)", - "rgba(95,158,160,100%)", - "rgb(37%,62%,63%)", - "rgba(37%,62%,63%,1)", - "rgba(37%,62%,63%,100%)", - "hsl(182,25%,50%)", - "hsla(182,25%,50%,1)", - "hsla(182,25%,50%,100%)", - "hwb(182,37%,37%)", - "hwb(182,37%,37%,1)", - "hwb(182,37%,37%,100%)", - ], - }, - "chartreuse": { - "hex": [ - "#7fff00", - "#ff7fff00", - ], - "func": [ - "rgb(127,255,0)", - "rgba(127,255,0,1)", - "rgba(127,255,0,100%)", - "rgb(50%,100%,0%)", - "rgba(50%,100%,0%,1)", - "rgba(50%,100%,0%,100%)", - "hsl(90,100%,50%)", - "hsla(90,100%,50%,1)", - "hsla(90,100%,50%,100%)", - "hwb(90,0%,0%)", - "hwb(90,0%,0%,1)", - "hwb(90,0%,0%,100%)", - ], - }, - "chocolate": { - "hex": [ - "#d2691e", - "#ffd2691e", - ], - "func": [ - "rgb(210,105,30)", - "rgba(210,105,30,1)", - "rgba(210,105,30,100%)", - "rgb(82%,41%,12%)", - "rgba(82%,41%,12%,1)", - "rgba(82%,41%,12%,100%)", - "hsl(25,75%,47%)", - "hsla(25,75%,47%,1)", - "hsla(25,75%,47%,100%)", - "hwb(25,12%,18%)", - "hwb(25,12%,18%,1)", - "hwb(25,12%,18%,100%)", - ], - }, - "coral": { - "hex": [ - "#ff7f50", - "#ffff7f50", - ], - "func": [ - "rgb(255,127,80)", - "rgba(255,127,80,1)", - "rgba(255,127,80,100%)", - "rgb(100%,50%,31%)", - "rgba(100%,50%,31%,1)", - "rgba(100%,50%,31%,100%)", - "hsl(16,100%,66%)", - "hsla(16,100%,66%,1)", - "hsla(16,100%,66%,100%)", - "hwb(16,31%,0%)", - "hwb(16,31%,0%,1)", - "hwb(16,31%,0%,100%)", - ], - }, - "cornflowerblue": { - "hex": [ - "#6495ed", - "#ff6495ed", - ], - "func": [ - "rgb(100,149,237)", - "rgba(100,149,237,1)", - "rgba(100,149,237,100%)", - "rgb(39%,58%,93%)", - "rgba(39%,58%,93%,1)", - "rgba(39%,58%,93%,100%)", - "hsl(219,79%,66%)", - "hsla(219,79%,66%,1)", - "hsla(219,79%,66%,100%)", - "hwb(219,39%,7%)", - "hwb(219,39%,7%,1)", - "hwb(219,39%,7%,100%)", - ], - }, - "cornsilk": { - "hex": [ - "#fff8dc", - "#fffff8dc", - ], - "func": [ - "rgb(255,248,220)", - "rgba(255,248,220,1)", - "rgba(255,248,220,100%)", - "rgb(100%,97%,86%)", - "rgba(100%,97%,86%,1)", - "rgba(100%,97%,86%,100%)", - "hsl(48,100%,93%)", - "hsla(48,100%,93%,1)", - "hsla(48,100%,93%,100%)", - "hwb(48,86%,0%)", - "hwb(48,86%,0%,1)", - "hwb(48,86%,0%,100%)", - ], - }, - "crimson": { - "hex": [ - "#dc143c", - "#ffdc143c", - ], - "func": [ - "rgb(220,20,60)", - "rgba(220,20,60,1)", - "rgba(220,20,60,100%)", - "rgb(86%,8%,24%)", - "rgba(86%,8%,24%,1)", - "rgba(86%,8%,24%,100%)", - "hsl(348,83%,47%)", - "hsla(348,83%,47%,1)", - "hsla(348,83%,47%,100%)", - "hwb(348,8%,14%)", - "hwb(348,8%,14%,1)", - "hwb(348,8%,14%,100%)", - ], - }, - "cyan": { - "hex": [ - "#00ffff", - "#ff00ffff", - "#0ff", - "#f0ff", - ], - "func": [ - "rgb(0,255,255)", - "rgba(0,255,255,1)", - "rgba(0,255,255,100%)", - "rgb(0%,100%,100%)", - "rgba(0%,100%,100%,1)", - "rgba(0%,100%,100%,100%)", - "hsl(180,100%,50%)", - "hsla(180,100%,50%,1)", - "hsla(180,100%,50%,100%)", - "hwb(180,0%,0%)", - "hwb(180,0%,0%,1)", - "hwb(180,0%,0%,100%)", - ], - }, - "darkblue": { - "hex": [ - "#00008b", - "#ff00008b", - ], - "func": [ - "rgb(0,0,139)", - "rgba(0,0,139,1)", - "rgba(0,0,139,100%)", - "rgb(0%,0%,55%)", - "rgba(0%,0%,55%,1)", - "rgba(0%,0%,55%,100%)", - "hsl(240,100%,27%)", - "hsla(240,100%,27%,1)", - "hsla(240,100%,27%,100%)", - "hwb(240,0%,45%)", - "hwb(240,0%,45%,1)", - "hwb(240,0%,45%,100%)", - ], - }, - "darkcyan": { - "hex": [ - "#008b8b", - "#ff008b8b", - ], - "func": [ - "rgb(0,139,139)", - "rgba(0,139,139,1)", - "rgba(0,139,139,100%)", - "rgb(0%,55%,55%)", - "rgba(0%,55%,55%,1)", - "rgba(0%,55%,55%,100%)", - "hsl(180,100%,27%)", - "hsla(180,100%,27%,1)", - "hsla(180,100%,27%,100%)", - "hwb(180,0%,45%)", - "hwb(180,0%,45%,1)", - "hwb(180,0%,45%,100%)", - ], - }, - "darkgoldenrod": { - "hex": [ - "#b8860b", - "#ffb8860b", - ], - "func": [ - "rgb(184,134,11)", - "rgba(184,134,11,1)", - "rgba(184,134,11,100%)", - "rgb(72%,53%,4%)", - "rgba(72%,53%,4%,1)", - "rgba(72%,53%,4%,100%)", - "hsl(43,89%,38%)", - "hsla(43,89%,38%,1)", - "hsla(43,89%,38%,100%)", - "hwb(43,4%,28%)", - "hwb(43,4%,28%,1)", - "hwb(43,4%,28%,100%)", - ], - }, - "darkgray": { - "hex": [ - "#a9a9a9", - "#ffa9a9a9", - ], - "func": [ - "rgb(169,169,169)", - "rgba(169,169,169,1)", - "rgba(169,169,169,100%)", - "rgb(66%,66%,66%)", - "rgba(66%,66%,66%,1)", - "rgba(66%,66%,66%,100%)", - "hsl(0,0%,66%)", - "hsla(0,0%,66%,1)", - "hsla(0,0%,66%,100%)", - "hwb(0,66%,34%)", - "hwb(0,66%,34%,1)", - "hwb(0,66%,34%,100%)", - ], - }, - "darkgreen": { - "hex": [ - "#006400", - "#ff006400", - ], - "func": [ - "rgb(0,100,0)", - "rgba(0,100,0,1)", - "rgba(0,100,0,100%)", - "rgb(0%,39%,0%)", - "rgba(0%,39%,0%,1)", - "rgba(0%,39%,0%,100%)", - "hsl(120,100%,20%)", - "hsla(120,100%,20%,1)", - "hsla(120,100%,20%,100%)", - "hwb(120,0%,61%)", - "hwb(120,0%,61%,1)", - "hwb(120,0%,61%,100%)", - ], - }, - "darkgrey": { - "hex": [ - "#a9a9a9", - "#ffa9a9a9", - ], - "func": [ - "rgb(169,169,169)", - "rgba(169,169,169,1)", - "rgba(169,169,169,100%)", - "rgb(66%,66%,66%)", - "rgba(66%,66%,66%,1)", - "rgba(66%,66%,66%,100%)", - "hsl(0,0%,66%)", - "hsla(0,0%,66%,1)", - "hsla(0,0%,66%,100%)", - "hwb(0,66%,34%)", - "hwb(0,66%,34%,1)", - "hwb(0,66%,34%,100%)", - "gray(169)", - "gray(169,1)", - "gray(169,100%)", - "gray(169%)", - "gray(169%,1)", - "gray(169%,100%)", - ], - }, - "darkkhaki": { - "hex": [ - "#bdb76b", - "#ffbdb76b", - ], - "func": [ - "rgb(189,183,107)", - "rgba(189,183,107,1)", - "rgba(189,183,107,100%)", - "rgb(74%,72%,42%)", - "rgba(74%,72%,42%,1)", - "rgba(74%,72%,42%,100%)", - "hsl(56,38%,58%)", - "hsla(56,38%,58%,1)", - "hsla(56,38%,58%,100%)", - "hwb(56,42%,26%)", - "hwb(56,42%,26%,1)", - "hwb(56,42%,26%,100%)", - ], - }, - "darkmagenta": { - "hex": [ - "#8b008b", - "#ff8b008b", - ], - "func": [ - "rgb(139,0,139)", - "rgba(139,0,139,1)", - "rgba(139,0,139,100%)", - "rgb(55%,0%,55%)", - "rgba(55%,0%,55%,1)", - "rgba(55%,0%,55%,100%)", - "hsl(300,100%,27%)", - "hsla(300,100%,27%,1)", - "hsla(300,100%,27%,100%)", - "hwb(300,0%,45%)", - "hwb(300,0%,45%,1)", - "hwb(300,0%,45%,100%)", - ], - }, - "darkolivegreen": { - "hex": [ - "#556b2f", - "#ff556b2f", - ], - "func": [ - "rgb(85,107,47)", - "rgba(85,107,47,1)", - "rgba(85,107,47,100%)", - "rgb(33%,42%,18%)", - "rgba(33%,42%,18%,1)", - "rgba(33%,42%,18%,100%)", - "hsl(82,39%,30%)", - "hsla(82,39%,30%,1)", - "hsla(82,39%,30%,100%)", - "hwb(82,18%,58%)", - "hwb(82,18%,58%,1)", - "hwb(82,18%,58%,100%)", - ], - }, - "darkorange": { - "hex": [ - "#ff8c00", - "#ffff8c00", - ], - "func": [ - "rgb(255,140,0)", - "rgba(255,140,0,1)", - "rgba(255,140,0,100%)", - "rgb(100%,55%,0%)", - "rgba(100%,55%,0%,1)", - "rgba(100%,55%,0%,100%)", - "hsl(33,100%,50%)", - "hsla(33,100%,50%,1)", - "hsla(33,100%,50%,100%)", - "hwb(33,0%,0%)", - "hwb(33,0%,0%,1)", - "hwb(33,0%,0%,100%)", - ], - }, - "darkorchid": { - "hex": [ - "#9932cc", - "#ff9932cc", - ], - "func": [ - "rgb(153,50,204)", - "rgba(153,50,204,1)", - "rgba(153,50,204,100%)", - "rgb(60%,20%,80%)", - "rgba(60%,20%,80%,1)", - "rgba(60%,20%,80%,100%)", - "hsl(280,61%,50%)", - "hsla(280,61%,50%,1)", - "hsla(280,61%,50%,100%)", - "hwb(280,20%,20%)", - "hwb(280,20%,20%,1)", - "hwb(280,20%,20%,100%)", - ], - }, - "darkred": { - "hex": [ - "#8b0000", - "#ff8b0000", - ], - "func": [ - "rgb(139,0,0)", - "rgba(139,0,0,1)", - "rgba(139,0,0,100%)", - "rgb(55%,0%,0%)", - "rgba(55%,0%,0%,1)", - "rgba(55%,0%,0%,100%)", - "hsl(0,100%,27%)", - "hsla(0,100%,27%,1)", - "hsla(0,100%,27%,100%)", - "hwb(0,0%,45%)", - "hwb(0,0%,45%,1)", - "hwb(0,0%,45%,100%)", - ], - }, - "darksalmon": { - "hex": [ - "#e9967a", - "#ffe9967a", - ], - "func": [ - "rgb(233,150,122)", - "rgba(233,150,122,1)", - "rgba(233,150,122,100%)", - "rgb(91%,59%,48%)", - "rgba(91%,59%,48%,1)", - "rgba(91%,59%,48%,100%)", - "hsl(15,72%,70%)", - "hsla(15,72%,70%,1)", - "hsla(15,72%,70%,100%)", - "hwb(15,48%,9%)", - "hwb(15,48%,9%,1)", - "hwb(15,48%,9%,100%)", - ], - }, - "darkseagreen": { - "hex": [ - "#8fbc8f", - "#ff8fbc8f", - ], - "func": [ - "rgb(143,188,143)", - "rgba(143,188,143,1)", - "rgba(143,188,143,100%)", - "rgb(56%,74%,56%)", - "rgba(56%,74%,56%,1)", - "rgba(56%,74%,56%,100%)", - "hsl(120,25%,65%)", - "hsla(120,25%,65%,1)", - "hsla(120,25%,65%,100%)", - "hwb(120,56%,26%)", - "hwb(120,56%,26%,1)", - "hwb(120,56%,26%,100%)", - ], - }, - "darkslateblue": { - "hex": [ - "#483d8b", - "#ff483d8b", - ], - "func": [ - "rgb(72,61,139)", - "rgba(72,61,139,1)", - "rgba(72,61,139,100%)", - "rgb(28%,24%,55%)", - "rgba(28%,24%,55%,1)", - "rgba(28%,24%,55%,100%)", - "hsl(248,39%,39%)", - "hsla(248,39%,39%,1)", - "hsla(248,39%,39%,100%)", - "hwb(248,24%,45%)", - "hwb(248,24%,45%,1)", - "hwb(248,24%,45%,100%)", - ], - }, - "darkslategray": { - "hex": [ - "#2f4f4f", - "#ff2f4f4f", - ], - "func": [ - "rgb(47,79,79)", - "rgba(47,79,79,1)", - "rgba(47,79,79,100%)", - "rgb(18%,31%,31%)", - "rgba(18%,31%,31%,1)", - "rgba(18%,31%,31%,100%)", - "hsl(180,25%,25%)", - "hsla(180,25%,25%,1)", - "hsla(180,25%,25%,100%)", - "hwb(180,18%,69%)", - "hwb(180,18%,69%,1)", - "hwb(180,18%,69%,100%)", - ], - }, - "darkslategrey": { - "hex": [ - "#2f4f4f", - "#ff2f4f4f", - ], - "func": [ - "rgb(47,79,79)", - "rgba(47,79,79,1)", - "rgba(47,79,79,100%)", - "rgb(18%,31%,31%)", - "rgba(18%,31%,31%,1)", - "rgba(18%,31%,31%,100%)", - "hsl(180,25%,25%)", - "hsla(180,25%,25%,1)", - "hsla(180,25%,25%,100%)", - "hwb(180,18%,69%)", - "hwb(180,18%,69%,1)", - "hwb(180,18%,69%,100%)", - ], - }, - "darkturquoise": { - "hex": [ - "#00ced1", - "#ff00ced1", - ], - "func": [ - "rgb(0,206,209)", - "rgba(0,206,209,1)", - "rgba(0,206,209,100%)", - "rgb(0%,81%,82%)", - "rgba(0%,81%,82%,1)", - "rgba(0%,81%,82%,100%)", - "hsl(181,100%,41%)", - "hsla(181,100%,41%,1)", - "hsla(181,100%,41%,100%)", - "hwb(181,0%,18%)", - "hwb(181,0%,18%,1)", - "hwb(181,0%,18%,100%)", - ], - }, - "darkviolet": { - "hex": [ - "#9400d3", - "#ff9400d3", - ], - "func": [ - "rgb(148,0,211)", - "rgba(148,0,211,1)", - "rgba(148,0,211,100%)", - "rgb(58%,0%,83%)", - "rgba(58%,0%,83%,1)", - "rgba(58%,0%,83%,100%)", - "hsl(282,100%,41%)", - "hsla(282,100%,41%,1)", - "hsla(282,100%,41%,100%)", - "hwb(282,0%,17%)", - "hwb(282,0%,17%,1)", - "hwb(282,0%,17%,100%)", - ], - }, - "deeppink": { - "hex": [ - "#ff1493", - "#ffff1493", - ], - "func": [ - "rgb(255,20,147)", - "rgba(255,20,147,1)", - "rgba(255,20,147,100%)", - "rgb(100%,8%,58%)", - "rgba(100%,8%,58%,1)", - "rgba(100%,8%,58%,100%)", - "hsl(328,100%,54%)", - "hsla(328,100%,54%,1)", - "hsla(328,100%,54%,100%)", - "hwb(328,8%,0%)", - "hwb(328,8%,0%,1)", - "hwb(328,8%,0%,100%)", - ], - }, - "deepskyblue": { - "hex": [ - "#00bfff", - "#ff00bfff", - ], - "func": [ - "rgb(0,191,255)", - "rgba(0,191,255,1)", - "rgba(0,191,255,100%)", - "rgb(0%,75%,100%)", - "rgba(0%,75%,100%,1)", - "rgba(0%,75%,100%,100%)", - "hsl(195,100%,50%)", - "hsla(195,100%,50%,1)", - "hsla(195,100%,50%,100%)", - "hwb(195,0%,0%)", - "hwb(195,0%,0%,1)", - "hwb(195,0%,0%,100%)", - ], - }, - "dimgray": { - "hex": [ - "#696969", - "#ff696969", - ], - "func": [ - "rgb(105,105,105)", - "rgba(105,105,105,1)", - "rgba(105,105,105,100%)", - "rgb(41%,41%,41%)", - "rgba(41%,41%,41%,1)", - "rgba(41%,41%,41%,100%)", - "hsl(0,0%,41%)", - "hsla(0,0%,41%,1)", - "hsla(0,0%,41%,100%)", - "hwb(0,41%,59%)", - "hwb(0,41%,59%,1)", - "hwb(0,41%,59%,100%)", - ], - }, - "dimgrey": { - "hex": [ - "#696969", - "#ff696969", - ], - "func": [ - "rgb(105,105,105)", - "rgba(105,105,105,1)", - "rgba(105,105,105,100%)", - "rgb(41%,41%,41%)", - "rgba(41%,41%,41%,1)", - "rgba(41%,41%,41%,100%)", - "hsl(0,0%,41%)", - "hsla(0,0%,41%,1)", - "hsla(0,0%,41%,100%)", - "hwb(0,41%,59%)", - "hwb(0,41%,59%,1)", - "hwb(0,41%,59%,100%)", - "gray(105)", - "gray(105,1)", - "gray(105,100%)", - "gray(105%)", - "gray(105%,1)", - "gray(105%,100%)", - ], - }, - "dodgerblue": { - "hex": [ - "#1e90ff", - "#ff1e90ff", - ], - "func": [ - "rgb(30,144,255)", - "rgba(30,144,255,1)", - "rgba(30,144,255,100%)", - "rgb(12%,56%,100%)", - "rgba(12%,56%,100%,1)", - "rgba(12%,56%,100%,100%)", - "hsl(210,100%,56%)", - "hsla(210,100%,56%,1)", - "hsla(210,100%,56%,100%)", - "hwb(210,12%,0%)", - "hwb(210,12%,0%,1)", - "hwb(210,12%,0%,100%)", - ], - }, - "firebrick": { - "hex": [ - "#b22222", - "#ffb22222", - ], - "func": [ - "rgb(178,34,34)", - "rgba(178,34,34,1)", - "rgba(178,34,34,100%)", - "rgb(70%,13%,13%)", - "rgba(70%,13%,13%,1)", - "rgba(70%,13%,13%,100%)", - "hsl(0,68%,42%)", - "hsla(0,68%,42%,1)", - "hsla(0,68%,42%,100%)", - "hwb(0,13%,30%)", - "hwb(0,13%,30%,1)", - "hwb(0,13%,30%,100%)", - ], - }, - "floralwhite": { - "hex": [ - "#fffaf0", - "#fffffaf0", - ], - "func": [ - "rgb(255,250,240)", - "rgba(255,250,240,1)", - "rgba(255,250,240,100%)", - "rgb(100%,98%,94%)", - "rgba(100%,98%,94%,1)", - "rgba(100%,98%,94%,100%)", - "hsl(40,100%,97%)", - "hsla(40,100%,97%,1)", - "hsla(40,100%,97%,100%)", - "hwb(40,94%,0%)", - "hwb(40,94%,0%,1)", - "hwb(40,94%,0%,100%)", - ], - }, - "forestgreen": { - "hex": [ - "#228b22", - "#ff228b22", - ], - "func": [ - "rgb(34,139,34)", - "rgba(34,139,34,1)", - "rgba(34,139,34,100%)", - "rgb(13%,55%,13%)", - "rgba(13%,55%,13%,1)", - "rgba(13%,55%,13%,100%)", - "hsl(120,61%,34%)", - "hsla(120,61%,34%,1)", - "hsla(120,61%,34%,100%)", - "hwb(120,13%,45%)", - "hwb(120,13%,45%,1)", - "hwb(120,13%,45%,100%)", - ], - }, - "fuchsia": { - "hex": [ - "#ff00ff", - "#ffff00ff", - "#f0f", - "#ff0f", - ], - "func": [ - "rgb(255,0,255)", - "rgba(255,0,255,1)", - "rgba(255,0,255,100%)", - "rgb(100%,0%,100%)", - "rgba(100%,0%,100%,1)", - "rgba(100%,0%,100%,100%)", - "hsl(300,100%,50%)", - "hsla(300,100%,50%,1)", - "hsla(300,100%,50%,100%)", - "hwb(300,0%,0%)", - "hwb(300,0%,0%,1)", - "hwb(300,0%,0%,100%)", - ], - }, - "gainsboro": { - "hex": [ - "#dcdcdc", - "#ffdcdcdc", - ], - "func": [ - "rgb(220,220,220)", - "rgba(220,220,220,1)", - "rgba(220,220,220,100%)", - "rgb(86%,86%,86%)", - "rgba(86%,86%,86%,1)", - "rgba(86%,86%,86%,100%)", - "hsl(0,0%,86%)", - "hsla(0,0%,86%,1)", - "hsla(0,0%,86%,100%)", - "hwb(0,86%,14%)", - "hwb(0,86%,14%,1)", - "hwb(0,86%,14%,100%)", - ], - }, - "ghostwhite": { - "hex": [ - "#f8f8ff", - "#fff8f8ff", - ], - "func": [ - "rgb(248,248,255)", - "rgba(248,248,255,1)", - "rgba(248,248,255,100%)", - "rgb(97%,97%,100%)", - "rgba(97%,97%,100%,1)", - "rgba(97%,97%,100%,100%)", - "hsl(240,100%,99%)", - "hsla(240,100%,99%,1)", - "hsla(240,100%,99%,100%)", - "hwb(240,97%,0%)", - "hwb(240,97%,0%,1)", - "hwb(240,97%,0%,100%)", - ], - }, - "gold": { - "hex": [ - "#ffd700", - "#ffffd700", - ], - "func": [ - "rgb(255,215,0)", - "rgba(255,215,0,1)", - "rgba(255,215,0,100%)", - "rgb(100%,84%,0%)", - "rgba(100%,84%,0%,1)", - "rgba(100%,84%,0%,100%)", - "hsl(51,100%,50%)", - "hsla(51,100%,50%,1)", - "hsla(51,100%,50%,100%)", - "hwb(51,0%,0%)", - "hwb(51,0%,0%,1)", - "hwb(51,0%,0%,100%)", - ], - }, - "goldenrod": { - "hex": [ - "#daa520", - "#ffdaa520", - ], - "func": [ - "rgb(218,165,32)", - "rgba(218,165,32,1)", - "rgba(218,165,32,100%)", - "rgb(85%,65%,13%)", - "rgba(85%,65%,13%,1)", - "rgba(85%,65%,13%,100%)", - "hsl(43,74%,49%)", - "hsla(43,74%,49%,1)", - "hsla(43,74%,49%,100%)", - "hwb(43,13%,15%)", - "hwb(43,13%,15%,1)", - "hwb(43,13%,15%,100%)", - ], - }, - "gray": { - "hex": [ - "#808080", - "#ff808080", - ], - "func": [ - "rgb(128,128,128)", - "rgba(128,128,128,1)", - "rgba(128,128,128,100%)", - "rgb(50%,50%,50%)", - "rgba(50%,50%,50%,1)", - "rgba(50%,50%,50%,100%)", - "hsl(0,0%,50%)", - "hsla(0,0%,50%,1)", - "hsla(0,0%,50%,100%)", - "hwb(0,50%,50%)", - "hwb(0,50%,50%,1)", - "hwb(0,50%,50%,100%)", - ], - }, - "green": { - "hex": [ - "#008000", - "#ff008000", - ], - "func": [ - "rgb(0,128,0)", - "rgba(0,128,0,1)", - "rgba(0,128,0,100%)", - "rgb(0%,50%,0%)", - "rgba(0%,50%,0%,1)", - "rgba(0%,50%,0%,100%)", - "hsl(120,100%,25%)", - "hsla(120,100%,25%,1)", - "hsla(120,100%,25%,100%)", - "hwb(120,0%,50%)", - "hwb(120,0%,50%,1)", - "hwb(120,0%,50%,100%)", - ], - }, - "greenyellow": { - "hex": [ - "#adff2f", - "#ffadff2f", - ], - "func": [ - "rgb(173,255,47)", - "rgba(173,255,47,1)", - "rgba(173,255,47,100%)", - "rgb(68%,100%,18%)", - "rgba(68%,100%,18%,1)", - "rgba(68%,100%,18%,100%)", - "hsl(84,100%,59%)", - "hsla(84,100%,59%,1)", - "hsla(84,100%,59%,100%)", - "hwb(84,18%,0%)", - "hwb(84,18%,0%,1)", - "hwb(84,18%,0%,100%)", - ], - }, - "grey": { - "hex": [ - "#808080", - "#ff808080", - ], - "func": [ - "rgb(128,128,128)", - "rgba(128,128,128,1)", - "rgba(128,128,128,100%)", - "rgb(50%,50%,50%)", - "rgba(50%,50%,50%,1)", - "rgba(50%,50%,50%,100%)", - "hsl(0,0%,50%)", - "hsla(0,0%,50%,1)", - "hsla(0,0%,50%,100%)", - "hwb(0,50%,50%)", - "hwb(0,50%,50%,1)", - "hwb(0,50%,50%,100%)", - "gray(128)", - "gray(128,1)", - "gray(128,100%)", - "gray(128%)", - "gray(128%,1)", - "gray(128%,100%)", - ], - }, - "honeydew": { - "hex": [ - "#f0fff0", - "#fff0fff0", - ], - "func": [ - "rgb(240,255,240)", - "rgba(240,255,240,1)", - "rgba(240,255,240,100%)", - "rgb(94%,100%,94%)", - "rgba(94%,100%,94%,1)", - "rgba(94%,100%,94%,100%)", - "hsl(120,100%,97%)", - "hsla(120,100%,97%,1)", - "hsla(120,100%,97%,100%)", - "hwb(120,94%,0%)", - "hwb(120,94%,0%,1)", - "hwb(120,94%,0%,100%)", - ], - }, - "hotpink": { - "hex": [ - "#ff69b4", - "#ffff69b4", - ], - "func": [ - "rgb(255,105,180)", - "rgba(255,105,180,1)", - "rgba(255,105,180,100%)", - "rgb(100%,41%,71%)", - "rgba(100%,41%,71%,1)", - "rgba(100%,41%,71%,100%)", - "hsl(330,100%,71%)", - "hsla(330,100%,71%,1)", - "hsla(330,100%,71%,100%)", - "hwb(330,41%,0%)", - "hwb(330,41%,0%,1)", - "hwb(330,41%,0%,100%)", - ], - }, - "indianred": { - "hex": [ - "#cd5c5c", - "#ffcd5c5c", - ], - "func": [ - "rgb(205,92,92)", - "rgba(205,92,92,1)", - "rgba(205,92,92,100%)", - "rgb(80%,36%,36%)", - "rgba(80%,36%,36%,1)", - "rgba(80%,36%,36%,100%)", - "hsl(0,53%,58%)", - "hsla(0,53%,58%,1)", - "hsla(0,53%,58%,100%)", - "hwb(0,36%,20%)", - "hwb(0,36%,20%,1)", - "hwb(0,36%,20%,100%)", - ], - }, - "indigo": { - "hex": [ - "#4b0082", - "#ff4b0082", - ], - "func": [ - "rgb(75,0,130)", - "rgba(75,0,130,1)", - "rgba(75,0,130,100%)", - "rgb(29%,0%,51%)", - "rgba(29%,0%,51%,1)", - "rgba(29%,0%,51%,100%)", - "hsl(275,100%,25%)", - "hsla(275,100%,25%,1)", - "hsla(275,100%,25%,100%)", - "hwb(275,0%,49%)", - "hwb(275,0%,49%,1)", - "hwb(275,0%,49%,100%)", - ], - }, - "ivory": { - "hex": [ - "#fffff0", - "#fffffff0", - ], - "func": [ - "rgb(255,255,240)", - "rgba(255,255,240,1)", - "rgba(255,255,240,100%)", - "rgb(100%,100%,94%)", - "rgba(100%,100%,94%,1)", - "rgba(100%,100%,94%,100%)", - "hsl(60,100%,97%)", - "hsla(60,100%,97%,1)", - "hsla(60,100%,97%,100%)", - "hwb(60,94%,0%)", - "hwb(60,94%,0%,1)", - "hwb(60,94%,0%,100%)", - ], - }, - "khaki": { - "hex": [ - "#f0e68c", - "#fff0e68c", - ], - "func": [ - "rgb(240,230,140)", - "rgba(240,230,140,1)", - "rgba(240,230,140,100%)", - "rgb(94%,90%,55%)", - "rgba(94%,90%,55%,1)", - "rgba(94%,90%,55%,100%)", - "hsl(54,77%,75%)", - "hsla(54,77%,75%,1)", - "hsla(54,77%,75%,100%)", - "hwb(54,55%,6%)", - "hwb(54,55%,6%,1)", - "hwb(54,55%,6%,100%)", - ], - }, - "lavender": { - "hex": [ - "#e6e6fa", - "#ffe6e6fa", - ], - "func": [ - "rgb(230,230,250)", - "rgba(230,230,250,1)", - "rgba(230,230,250,100%)", - "rgb(90%,90%,98%)", - "rgba(90%,90%,98%,1)", - "rgba(90%,90%,98%,100%)", - "hsl(240,67%,94%)", - "hsla(240,67%,94%,1)", - "hsla(240,67%,94%,100%)", - "hwb(240,90%,2%)", - "hwb(240,90%,2%,1)", - "hwb(240,90%,2%,100%)", - ], - }, - "lavenderblush": { - "hex": [ - "#fff0f5", - "#fffff0f5", - ], - "func": [ - "rgb(255,240,245)", - "rgba(255,240,245,1)", - "rgba(255,240,245,100%)", - "rgb(100%,94%,96%)", - "rgba(100%,94%,96%,1)", - "rgba(100%,94%,96%,100%)", - "hsl(340,100%,97%)", - "hsla(340,100%,97%,1)", - "hsla(340,100%,97%,100%)", - "hwb(340,94%,0%)", - "hwb(340,94%,0%,1)", - "hwb(340,94%,0%,100%)", - ], - }, - "lawngreen": { - "hex": [ - "#7cfc00", - "#ff7cfc00", - ], - "func": [ - "rgb(124,252,0)", - "rgba(124,252,0,1)", - "rgba(124,252,0,100%)", - "rgb(49%,99%,0%)", - "rgba(49%,99%,0%,1)", - "rgba(49%,99%,0%,100%)", - "hsl(90,100%,49%)", - "hsla(90,100%,49%,1)", - "hsla(90,100%,49%,100%)", - "hwb(90,0%,1%)", - "hwb(90,0%,1%,1)", - "hwb(90,0%,1%,100%)", - ], - }, - "lemonchiffon": { - "hex": [ - "#fffacd", - "#fffffacd", - ], - "func": [ - "rgb(255,250,205)", - "rgba(255,250,205,1)", - "rgba(255,250,205,100%)", - "rgb(100%,98%,80%)", - "rgba(100%,98%,80%,1)", - "rgba(100%,98%,80%,100%)", - "hsl(54,100%,90%)", - "hsla(54,100%,90%,1)", - "hsla(54,100%,90%,100%)", - "hwb(54,80%,0%)", - "hwb(54,80%,0%,1)", - "hwb(54,80%,0%,100%)", - ], - }, - "lightblue": { - "hex": [ - "#add8e6", - "#ffadd8e6", - ], - "func": [ - "rgb(173,216,230)", - "rgba(173,216,230,1)", - "rgba(173,216,230,100%)", - "rgb(68%,85%,90%)", - "rgba(68%,85%,90%,1)", - "rgba(68%,85%,90%,100%)", - "hsl(195,53%,79%)", - "hsla(195,53%,79%,1)", - "hsla(195,53%,79%,100%)", - "hwb(195,68%,10%)", - "hwb(195,68%,10%,1)", - "hwb(195,68%,10%,100%)", - ], - }, - "lightcoral": { - "hex": [ - "#f08080", - "#fff08080", - ], - "func": [ - "rgb(240,128,128)", - "rgba(240,128,128,1)", - "rgba(240,128,128,100%)", - "rgb(94%,50%,50%)", - "rgba(94%,50%,50%,1)", - "rgba(94%,50%,50%,100%)", - "hsl(0,79%,72%)", - "hsla(0,79%,72%,1)", - "hsla(0,79%,72%,100%)", - "hwb(0,50%,6%)", - "hwb(0,50%,6%,1)", - "hwb(0,50%,6%,100%)", - ], - }, - "lightcyan": { - "hex": [ - "#e0ffff", - "#ffe0ffff", - ], - "func": [ - "rgb(224,255,255)", - "rgba(224,255,255,1)", - "rgba(224,255,255,100%)", - "rgb(88%,100%,100%)", - "rgba(88%,100%,100%,1)", - "rgba(88%,100%,100%,100%)", - "hsl(180,100%,94%)", - "hsla(180,100%,94%,1)", - "hsla(180,100%,94%,100%)", - "hwb(180,88%,0%)", - "hwb(180,88%,0%,1)", - "hwb(180,88%,0%,100%)", - ], - }, - "lightgoldenrodyellow": { - "hex": [ - "#fafad2", - "#fffafad2", - ], - "func": [ - "rgb(250,250,210)", - "rgba(250,250,210,1)", - "rgba(250,250,210,100%)", - "rgb(98%,98%,82%)", - "rgba(98%,98%,82%,1)", - "rgba(98%,98%,82%,100%)", - "hsl(60,80%,90%)", - "hsla(60,80%,90%,1)", - "hsla(60,80%,90%,100%)", - "hwb(60,82%,2%)", - "hwb(60,82%,2%,1)", - "hwb(60,82%,2%,100%)", - ], - }, - "lightgray": { - "hex": [ - "#d3d3d3", - "#ffd3d3d3", - ], - "func": [ - "rgb(211,211,211)", - "rgba(211,211,211,1)", - "rgba(211,211,211,100%)", - "rgb(83%,83%,83%)", - "rgba(83%,83%,83%,1)", - "rgba(83%,83%,83%,100%)", - "hsl(0,0%,83%)", - "hsla(0,0%,83%,1)", - "hsla(0,0%,83%,100%)", - "hwb(0,83%,17%)", - "hwb(0,83%,17%,1)", - "hwb(0,83%,17%,100%)", - ], - }, - "lightgreen": { - "hex": [ - "#90ee90", - "#ff90ee90", - ], - "func": [ - "rgb(144,238,144)", - "rgba(144,238,144,1)", - "rgba(144,238,144,100%)", - "rgb(56%,93%,56%)", - "rgba(56%,93%,56%,1)", - "rgba(56%,93%,56%,100%)", - "hsl(120,73%,75%)", - "hsla(120,73%,75%,1)", - "hsla(120,73%,75%,100%)", - "hwb(120,56%,7%)", - "hwb(120,56%,7%,1)", - "hwb(120,56%,7%,100%)", - ], - }, - "lightgrey": { - "hex": [ - "#d3d3d3", - "#ffd3d3d3", - ], - "func": [ - "rgb(211,211,211)", - "rgba(211,211,211,1)", - "rgba(211,211,211,100%)", - "rgb(83%,83%,83%)", - "rgba(83%,83%,83%,1)", - "rgba(83%,83%,83%,100%)", - "hsl(0,0%,83%)", - "hsla(0,0%,83%,1)", - "hsla(0,0%,83%,100%)", - "hwb(0,83%,17%)", - "hwb(0,83%,17%,1)", - "hwb(0,83%,17%,100%)", - "gray(211)", - "gray(211,1)", - "gray(211,100%)", - "gray(211%)", - "gray(211%,1)", - "gray(211%,100%)", - ], - }, - "lightpink": { - "hex": [ - "#ffb6c1", - "#ffffb6c1", - ], - "func": [ - "rgb(255,182,193)", - "rgba(255,182,193,1)", - "rgba(255,182,193,100%)", - "rgb(100%,71%,76%)", - "rgba(100%,71%,76%,1)", - "rgba(100%,71%,76%,100%)", - "hsl(351,100%,86%)", - "hsla(351,100%,86%,1)", - "hsla(351,100%,86%,100%)", - "hwb(351,71%,0%)", - "hwb(351,71%,0%,1)", - "hwb(351,71%,0%,100%)", - ], - }, - "lightsalmon": { - "hex": [ - "#ffa07a", - "#ffffa07a", - ], - "func": [ - "rgb(255,160,122)", - "rgba(255,160,122,1)", - "rgba(255,160,122,100%)", - "rgb(100%,63%,48%)", - "rgba(100%,63%,48%,1)", - "rgba(100%,63%,48%,100%)", - "hsl(17,100%,74%)", - "hsla(17,100%,74%,1)", - "hsla(17,100%,74%,100%)", - "hwb(17,48%,0%)", - "hwb(17,48%,0%,1)", - "hwb(17,48%,0%,100%)", - ], - }, - "lightseagreen": { - "hex": [ - "#20b2aa", - "#ff20b2aa", - ], - "func": [ - "rgb(32,178,170)", - "rgba(32,178,170,1)", - "rgba(32,178,170,100%)", - "rgb(13%,70%,67%)", - "rgba(13%,70%,67%,1)", - "rgba(13%,70%,67%,100%)", - "hsl(177,70%,41%)", - "hsla(177,70%,41%,1)", - "hsla(177,70%,41%,100%)", - "hwb(177,13%,30%)", - "hwb(177,13%,30%,1)", - "hwb(177,13%,30%,100%)", - ], - }, - "lightskyblue": { - "hex": [ - "#87cefa", - "#ff87cefa", - ], - "func": [ - "rgb(135,206,250)", - "rgba(135,206,250,1)", - "rgba(135,206,250,100%)", - "rgb(53%,81%,98%)", - "rgba(53%,81%,98%,1)", - "rgba(53%,81%,98%,100%)", - "hsl(203,92%,75%)", - "hsla(203,92%,75%,1)", - "hsla(203,92%,75%,100%)", - "hwb(203,53%,2%)", - "hwb(203,53%,2%,1)", - "hwb(203,53%,2%,100%)", - ], - }, - "lightslategray": { - "hex": [ - "#778899", - "#ff778899", - "#789", - "#f789", - ], - "func": [ - "rgb(119,136,153)", - "rgba(119,136,153,1)", - "rgba(119,136,153,100%)", - "rgb(47%,53%,60%)", - "rgba(47%,53%,60%,1)", - "rgba(47%,53%,60%,100%)", - "hsl(210,14%,53%)", - "hsla(210,14%,53%,1)", - "hsla(210,14%,53%,100%)", - "hwb(210,47%,40%)", - "hwb(210,47%,40%,1)", - "hwb(210,47%,40%,100%)", - ], - }, - "lightslategrey": { - "hex": [ - "#778899", - "#ff778899", - "#789", - "#f789", - ], - "func": [ - "rgb(119,136,153)", - "rgba(119,136,153,1)", - "rgba(119,136,153,100%)", - "rgb(47%,53%,60%)", - "rgba(47%,53%,60%,1)", - "rgba(47%,53%,60%,100%)", - "hsl(210,14%,53%)", - "hsla(210,14%,53%,1)", - "hsla(210,14%,53%,100%)", - "hwb(210,47%,40%)", - "hwb(210,47%,40%,1)", - "hwb(210,47%,40%,100%)", - ], - }, - "lightsteelblue": { - "hex": [ - "#b0c4de", - "#ffb0c4de", - ], - "func": [ - "rgb(176,196,222)", - "rgba(176,196,222,1)", - "rgba(176,196,222,100%)", - "rgb(69%,77%,87%)", - "rgba(69%,77%,87%,1)", - "rgba(69%,77%,87%,100%)", - "hsl(214,41%,78%)", - "hsla(214,41%,78%,1)", - "hsla(214,41%,78%,100%)", - "hwb(214,69%,13%)", - "hwb(214,69%,13%,1)", - "hwb(214,69%,13%,100%)", - ], - }, - "lightyellow": { - "hex": [ - "#ffffe0", - "#ffffffe0", - ], - "func": [ - "rgb(255,255,224)", - "rgba(255,255,224,1)", - "rgba(255,255,224,100%)", - "rgb(100%,100%,88%)", - "rgba(100%,100%,88%,1)", - "rgba(100%,100%,88%,100%)", - "hsl(60,100%,94%)", - "hsla(60,100%,94%,1)", - "hsla(60,100%,94%,100%)", - "hwb(60,88%,0%)", - "hwb(60,88%,0%,1)", - "hwb(60,88%,0%,100%)", - ], - }, - "lime": { - "hex": [ - "#00ff00", - "#ff00ff00", - "#0f0", - "#f0f0", - ], - "func": [ - "rgb(0,255,0)", - "rgba(0,255,0,1)", - "rgba(0,255,0,100%)", - "rgb(0%,100%,0%)", - "rgba(0%,100%,0%,1)", - "rgba(0%,100%,0%,100%)", - "hsl(120,100%,50%)", - "hsla(120,100%,50%,1)", - "hsla(120,100%,50%,100%)", - "hwb(120,0%,0%)", - "hwb(120,0%,0%,1)", - "hwb(120,0%,0%,100%)", - ], - }, - "limegreen": { - "hex": [ - "#32cd32", - "#ff32cd32", - ], - "func": [ - "rgb(50,205,50)", - "rgba(50,205,50,1)", - "rgba(50,205,50,100%)", - "rgb(20%,80%,20%)", - "rgba(20%,80%,20%,1)", - "rgba(20%,80%,20%,100%)", - "hsl(120,61%,50%)", - "hsla(120,61%,50%,1)", - "hsla(120,61%,50%,100%)", - "hwb(120,20%,20%)", - "hwb(120,20%,20%,1)", - "hwb(120,20%,20%,100%)", - ], - }, - "linen": { - "hex": [ - "#faf0e6", - "#fffaf0e6", - ], - "func": [ - "rgb(250,240,230)", - "rgba(250,240,230,1)", - "rgba(250,240,230,100%)", - "rgb(98%,94%,90%)", - "rgba(98%,94%,90%,1)", - "rgba(98%,94%,90%,100%)", - "hsl(30,67%,94%)", - "hsla(30,67%,94%,1)", - "hsla(30,67%,94%,100%)", - "hwb(30,90%,2%)", - "hwb(30,90%,2%,1)", - "hwb(30,90%,2%,100%)", - ], - }, - "magenta": { - "hex": [ - "#ff00ff", - "#ffff00ff", - "#f0f", - "#ff0f", - ], - "func": [ - "rgb(255,0,255)", - "rgba(255,0,255,1)", - "rgba(255,0,255,100%)", - "rgb(100%,0%,100%)", - "rgba(100%,0%,100%,1)", - "rgba(100%,0%,100%,100%)", - "hsl(300,100%,50%)", - "hsla(300,100%,50%,1)", - "hsla(300,100%,50%,100%)", - "hwb(300,0%,0%)", - "hwb(300,0%,0%,1)", - "hwb(300,0%,0%,100%)", - ], - }, - "maroon": { - "hex": [ - "#800000", - "#ff800000", - ], - "func": [ - "rgb(128,0,0)", - "rgba(128,0,0,1)", - "rgba(128,0,0,100%)", - "rgb(50%,0%,0%)", - "rgba(50%,0%,0%,1)", - "rgba(50%,0%,0%,100%)", - "hsl(0,100%,25%)", - "hsla(0,100%,25%,1)", - "hsla(0,100%,25%,100%)", - "hwb(0,0%,50%)", - "hwb(0,0%,50%,1)", - "hwb(0,0%,50%,100%)", - ], - }, - "mediumaquamarine": { - "hex": [ - "#66cdaa", - "#ff66cdaa", - ], - "func": [ - "rgb(102,205,170)", - "rgba(102,205,170,1)", - "rgba(102,205,170,100%)", - "rgb(40%,80%,67%)", - "rgba(40%,80%,67%,1)", - "rgba(40%,80%,67%,100%)", - "hsl(160,51%,60%)", - "hsla(160,51%,60%,1)", - "hsla(160,51%,60%,100%)", - "hwb(160,40%,20%)", - "hwb(160,40%,20%,1)", - "hwb(160,40%,20%,100%)", - ], - }, - "mediumblue": { - "hex": [ - "#0000cd", - "#ff0000cd", - ], - "func": [ - "rgb(0,0,205)", - "rgba(0,0,205,1)", - "rgba(0,0,205,100%)", - "rgb(0%,0%,80%)", - "rgba(0%,0%,80%,1)", - "rgba(0%,0%,80%,100%)", - "hsl(240,100%,40%)", - "hsla(240,100%,40%,1)", - "hsla(240,100%,40%,100%)", - "hwb(240,0%,20%)", - "hwb(240,0%,20%,1)", - "hwb(240,0%,20%,100%)", - ], - }, - "mediumorchid": { - "hex": [ - "#ba55d3", - "#ffba55d3", - ], - "func": [ - "rgb(186,85,211)", - "rgba(186,85,211,1)", - "rgba(186,85,211,100%)", - "rgb(73%,33%,83%)", - "rgba(73%,33%,83%,1)", - "rgba(73%,33%,83%,100%)", - "hsl(288,59%,58%)", - "hsla(288,59%,58%,1)", - "hsla(288,59%,58%,100%)", - "hwb(288,33%,17%)", - "hwb(288,33%,17%,1)", - "hwb(288,33%,17%,100%)", - ], - }, - "mediumpurple": { - "hex": [ - "#9370db", - "#ff9370db", - ], - "func": [ - "rgb(147,112,219)", - "rgba(147,112,219,1)", - "rgba(147,112,219,100%)", - "rgb(58%,44%,86%)", - "rgba(58%,44%,86%,1)", - "rgba(58%,44%,86%,100%)", - "hsl(260,60%,65%)", - "hsla(260,60%,65%,1)", - "hsla(260,60%,65%,100%)", - "hwb(260,44%,14%)", - "hwb(260,44%,14%,1)", - "hwb(260,44%,14%,100%)", - ], - }, - "mediumseagreen": { - "hex": [ - "#3cb371", - "#ff3cb371", - ], - "func": [ - "rgb(60,179,113)", - "rgba(60,179,113,1)", - "rgba(60,179,113,100%)", - "rgb(24%,70%,44%)", - "rgba(24%,70%,44%,1)", - "rgba(24%,70%,44%,100%)", - "hsl(147,50%,47%)", - "hsla(147,50%,47%,1)", - "hsla(147,50%,47%,100%)", - "hwb(147,24%,30%)", - "hwb(147,24%,30%,1)", - "hwb(147,24%,30%,100%)", - ], - }, - "mediumslateblue": { - "hex": [ - "#7b68ee", - "#ff7b68ee", - ], - "func": [ - "rgb(123,104,238)", - "rgba(123,104,238,1)", - "rgba(123,104,238,100%)", - "rgb(48%,41%,93%)", - "rgba(48%,41%,93%,1)", - "rgba(48%,41%,93%,100%)", - "hsl(249,80%,67%)", - "hsla(249,80%,67%,1)", - "hsla(249,80%,67%,100%)", - "hwb(249,41%,7%)", - "hwb(249,41%,7%,1)", - "hwb(249,41%,7%,100%)", - ], - }, - "mediumspringgreen": { - "hex": [ - "#00fa9a", - "#ff00fa9a", - ], - "func": [ - "rgb(0,250,154)", - "rgba(0,250,154,1)", - "rgba(0,250,154,100%)", - "rgb(0%,98%,60%)", - "rgba(0%,98%,60%,1)", - "rgba(0%,98%,60%,100%)", - "hsl(157,100%,49%)", - "hsla(157,100%,49%,1)", - "hsla(157,100%,49%,100%)", - "hwb(157,0%,2%)", - "hwb(157,0%,2%,1)", - "hwb(157,0%,2%,100%)", - ], - }, - "mediumturquoise": { - "hex": [ - "#48d1cc", - "#ff48d1cc", - ], - "func": [ - "rgb(72,209,204)", - "rgba(72,209,204,1)", - "rgba(72,209,204,100%)", - "rgb(28%,82%,80%)", - "rgba(28%,82%,80%,1)", - "rgba(28%,82%,80%,100%)", - "hsl(178,60%,55%)", - "hsla(178,60%,55%,1)", - "hsla(178,60%,55%,100%)", - "hwb(178,28%,18%)", - "hwb(178,28%,18%,1)", - "hwb(178,28%,18%,100%)", - ], - }, - "mediumvioletred": { - "hex": [ - "#c71585", - "#ffc71585", - ], - "func": [ - "rgb(199,21,133)", - "rgba(199,21,133,1)", - "rgba(199,21,133,100%)", - "rgb(78%,8%,52%)", - "rgba(78%,8%,52%,1)", - "rgba(78%,8%,52%,100%)", - "hsl(322,81%,43%)", - "hsla(322,81%,43%,1)", - "hsla(322,81%,43%,100%)", - "hwb(322,8%,22%)", - "hwb(322,8%,22%,1)", - "hwb(322,8%,22%,100%)", - ], - }, - "midnightblue": { - "hex": [ - "#191970", - "#ff191970", - ], - "func": [ - "rgb(25,25,112)", - "rgba(25,25,112,1)", - "rgba(25,25,112,100%)", - "rgb(10%,10%,44%)", - "rgba(10%,10%,44%,1)", - "rgba(10%,10%,44%,100%)", - "hsl(240,64%,27%)", - "hsla(240,64%,27%,1)", - "hsla(240,64%,27%,100%)", - "hwb(240,10%,56%)", - "hwb(240,10%,56%,1)", - "hwb(240,10%,56%,100%)", - ], - }, - "mintcream": { - "hex": [ - "#f5fffa", - "#fff5fffa", - ], - "func": [ - "rgb(245,255,250)", - "rgba(245,255,250,1)", - "rgba(245,255,250,100%)", - "rgb(96%,100%,98%)", - "rgba(96%,100%,98%,1)", - "rgba(96%,100%,98%,100%)", - "hsl(150,100%,98%)", - "hsla(150,100%,98%,1)", - "hsla(150,100%,98%,100%)", - "hwb(150,96%,0%)", - "hwb(150,96%,0%,1)", - "hwb(150,96%,0%,100%)", - ], - }, - "mistyrose": { - "hex": [ - "#ffe4e1", - "#ffffe4e1", - ], - "func": [ - "rgb(255,228,225)", - "rgba(255,228,225,1)", - "rgba(255,228,225,100%)", - "rgb(100%,89%,88%)", - "rgba(100%,89%,88%,1)", - "rgba(100%,89%,88%,100%)", - "hsl(6,100%,94%)", - "hsla(6,100%,94%,1)", - "hsla(6,100%,94%,100%)", - "hwb(6,88%,0%)", - "hwb(6,88%,0%,1)", - "hwb(6,88%,0%,100%)", - ], - }, - "moccasin": { - "hex": [ - "#ffe4b5", - "#ffffe4b5", - ], - "func": [ - "rgb(255,228,181)", - "rgba(255,228,181,1)", - "rgba(255,228,181,100%)", - "rgb(100%,89%,71%)", - "rgba(100%,89%,71%,1)", - "rgba(100%,89%,71%,100%)", - "hsl(38,100%,85%)", - "hsla(38,100%,85%,1)", - "hsla(38,100%,85%,100%)", - "hwb(38,71%,0%)", - "hwb(38,71%,0%,1)", - "hwb(38,71%,0%,100%)", - ], - }, - "navajowhite": { - "hex": [ - "#ffdead", - "#ffffdead", - ], - "func": [ - "rgb(255,222,173)", - "rgba(255,222,173,1)", - "rgba(255,222,173,100%)", - "rgb(100%,87%,68%)", - "rgba(100%,87%,68%,1)", - "rgba(100%,87%,68%,100%)", - "hsl(36,100%,84%)", - "hsla(36,100%,84%,1)", - "hsla(36,100%,84%,100%)", - "hwb(36,68%,0%)", - "hwb(36,68%,0%,1)", - "hwb(36,68%,0%,100%)", - ], - }, - "navy": { - "hex": [ - "#000080", - "#ff000080", - ], - "func": [ - "rgb(0,0,128)", - "rgba(0,0,128,1)", - "rgba(0,0,128,100%)", - "rgb(0%,0%,50%)", - "rgba(0%,0%,50%,1)", - "rgba(0%,0%,50%,100%)", - "hsl(240,100%,25%)", - "hsla(240,100%,25%,1)", - "hsla(240,100%,25%,100%)", - "hwb(240,0%,50%)", - "hwb(240,0%,50%,1)", - "hwb(240,0%,50%,100%)", - ], - }, - "oldlace": { - "hex": [ - "#fdf5e6", - "#fffdf5e6", - ], - "func": [ - "rgb(253,245,230)", - "rgba(253,245,230,1)", - "rgba(253,245,230,100%)", - "rgb(99%,96%,90%)", - "rgba(99%,96%,90%,1)", - "rgba(99%,96%,90%,100%)", - "hsl(39,85%,95%)", - "hsla(39,85%,95%,1)", - "hsla(39,85%,95%,100%)", - "hwb(39,90%,1%)", - "hwb(39,90%,1%,1)", - "hwb(39,90%,1%,100%)", - ], - }, - "olive": { - "hex": [ - "#808000", - "#ff808000", - ], - "func": [ - "rgb(128,128,0)", - "rgba(128,128,0,1)", - "rgba(128,128,0,100%)", - "rgb(50%,50%,0%)", - "rgba(50%,50%,0%,1)", - "rgba(50%,50%,0%,100%)", - "hsl(60,100%,25%)", - "hsla(60,100%,25%,1)", - "hsla(60,100%,25%,100%)", - "hwb(60,0%,50%)", - "hwb(60,0%,50%,1)", - "hwb(60,0%,50%,100%)", - ], - }, - "olivedrab": { - "hex": [ - "#6b8e23", - "#ff6b8e23", - ], - "func": [ - "rgb(107,142,35)", - "rgba(107,142,35,1)", - "rgba(107,142,35,100%)", - "rgb(42%,56%,14%)", - "rgba(42%,56%,14%,1)", - "rgba(42%,56%,14%,100%)", - "hsl(80,60%,35%)", - "hsla(80,60%,35%,1)", - "hsla(80,60%,35%,100%)", - "hwb(80,14%,44%)", - "hwb(80,14%,44%,1)", - "hwb(80,14%,44%,100%)", - ], - }, - "orange": { - "hex": [ - "#ffa500", - "#ffffa500", - ], - "func": [ - "rgb(255,165,0)", - "rgba(255,165,0,1)", - "rgba(255,165,0,100%)", - "rgb(100%,65%,0%)", - "rgba(100%,65%,0%,1)", - "rgba(100%,65%,0%,100%)", - "hsl(39,100%,50%)", - "hsla(39,100%,50%,1)", - "hsla(39,100%,50%,100%)", - "hwb(39,0%,0%)", - "hwb(39,0%,0%,1)", - "hwb(39,0%,0%,100%)", - ], - }, - "orangered": { - "hex": [ - "#ff4500", - "#ffff4500", - ], - "func": [ - "rgb(255,69,0)", - "rgba(255,69,0,1)", - "rgba(255,69,0,100%)", - "rgb(100%,27%,0%)", - "rgba(100%,27%,0%,1)", - "rgba(100%,27%,0%,100%)", - "hsl(16,100%,50%)", - "hsla(16,100%,50%,1)", - "hsla(16,100%,50%,100%)", - "hwb(16,0%,0%)", - "hwb(16,0%,0%,1)", - "hwb(16,0%,0%,100%)", - ], - }, - "orchid": { - "hex": [ - "#da70d6", - "#ffda70d6", - ], - "func": [ - "rgb(218,112,214)", - "rgba(218,112,214,1)", - "rgba(218,112,214,100%)", - "rgb(85%,44%,84%)", - "rgba(85%,44%,84%,1)", - "rgba(85%,44%,84%,100%)", - "hsl(302,59%,65%)", - "hsla(302,59%,65%,1)", - "hsla(302,59%,65%,100%)", - "hwb(302,44%,15%)", - "hwb(302,44%,15%,1)", - "hwb(302,44%,15%,100%)", - ], - }, - "palegoldenrod": { - "hex": [ - "#eee8aa", - "#ffeee8aa", - ], - "func": [ - "rgb(238,232,170)", - "rgba(238,232,170,1)", - "rgba(238,232,170,100%)", - "rgb(93%,91%,67%)", - "rgba(93%,91%,67%,1)", - "rgba(93%,91%,67%,100%)", - "hsl(55,67%,80%)", - "hsla(55,67%,80%,1)", - "hsla(55,67%,80%,100%)", - "hwb(55,67%,7%)", - "hwb(55,67%,7%,1)", - "hwb(55,67%,7%,100%)", - ], - }, - "palegreen": { - "hex": [ - "#98fb98", - "#ff98fb98", - ], - "func": [ - "rgb(152,251,152)", - "rgba(152,251,152,1)", - "rgba(152,251,152,100%)", - "rgb(60%,98%,60%)", - "rgba(60%,98%,60%,1)", - "rgba(60%,98%,60%,100%)", - "hsl(120,93%,79%)", - "hsla(120,93%,79%,1)", - "hsla(120,93%,79%,100%)", - "hwb(120,60%,2%)", - "hwb(120,60%,2%,1)", - "hwb(120,60%,2%,100%)", - ], - }, - "paleturquoise": { - "hex": [ - "#afeeee", - "#ffafeeee", - ], - "func": [ - "rgb(175,238,238)", - "rgba(175,238,238,1)", - "rgba(175,238,238,100%)", - "rgb(69%,93%,93%)", - "rgba(69%,93%,93%,1)", - "rgba(69%,93%,93%,100%)", - "hsl(180,65%,81%)", - "hsla(180,65%,81%,1)", - "hsla(180,65%,81%,100%)", - "hwb(180,69%,7%)", - "hwb(180,69%,7%,1)", - "hwb(180,69%,7%,100%)", - ], - }, - "palevioletred": { - "hex": [ - "#db7093", - "#ffdb7093", - ], - "func": [ - "rgb(219,112,147)", - "rgba(219,112,147,1)", - "rgba(219,112,147,100%)", - "rgb(86%,44%,58%)", - "rgba(86%,44%,58%,1)", - "rgba(86%,44%,58%,100%)", - "hsl(340,60%,65%)", - "hsla(340,60%,65%,1)", - "hsla(340,60%,65%,100%)", - "hwb(340,44%,14%)", - "hwb(340,44%,14%,1)", - "hwb(340,44%,14%,100%)", - ], - }, - "papayawhip": { - "hex": [ - "#ffefd5", - "#ffffefd5", - ], - "func": [ - "rgb(255,239,213)", - "rgba(255,239,213,1)", - "rgba(255,239,213,100%)", - "rgb(100%,94%,84%)", - "rgba(100%,94%,84%,1)", - "rgba(100%,94%,84%,100%)", - "hsl(37,100%,92%)", - "hsla(37,100%,92%,1)", - "hsla(37,100%,92%,100%)", - "hwb(37,84%,0%)", - "hwb(37,84%,0%,1)", - "hwb(37,84%,0%,100%)", - ], - }, - "peachpuff": { - "hex": [ - "#ffdab9", - "#ffffdab9", - ], - "func": [ - "rgb(255,218,185)", - "rgba(255,218,185,1)", - "rgba(255,218,185,100%)", - "rgb(100%,85%,73%)", - "rgba(100%,85%,73%,1)", - "rgba(100%,85%,73%,100%)", - "hsl(28,100%,86%)", - "hsla(28,100%,86%,1)", - "hsla(28,100%,86%,100%)", - "hwb(28,73%,0%)", - "hwb(28,73%,0%,1)", - "hwb(28,73%,0%,100%)", - ], - }, - "peru": { - "hex": [ - "#cd853f", - "#ffcd853f", - ], - "func": [ - "rgb(205,133,63)", - "rgba(205,133,63,1)", - "rgba(205,133,63,100%)", - "rgb(80%,52%,25%)", - "rgba(80%,52%,25%,1)", - "rgba(80%,52%,25%,100%)", - "hsl(30,59%,53%)", - "hsla(30,59%,53%,1)", - "hsla(30,59%,53%,100%)", - "hwb(30,25%,20%)", - "hwb(30,25%,20%,1)", - "hwb(30,25%,20%,100%)", - ], - }, - "pink": { - "hex": [ - "#ffc0cb", - "#ffffc0cb", - ], - "func": [ - "rgb(255,192,203)", - "rgba(255,192,203,1)", - "rgba(255,192,203,100%)", - "rgb(100%,75%,80%)", - "rgba(100%,75%,80%,1)", - "rgba(100%,75%,80%,100%)", - "hsl(350,100%,88%)", - "hsla(350,100%,88%,1)", - "hsla(350,100%,88%,100%)", - "hwb(350,75%,0%)", - "hwb(350,75%,0%,1)", - "hwb(350,75%,0%,100%)", - ], - }, - "plum": { - "hex": [ - "#dda0dd", - "#ffdda0dd", - ], - "func": [ - "rgb(221,160,221)", - "rgba(221,160,221,1)", - "rgba(221,160,221,100%)", - "rgb(87%,63%,87%)", - "rgba(87%,63%,87%,1)", - "rgba(87%,63%,87%,100%)", - "hsl(300,47%,75%)", - "hsla(300,47%,75%,1)", - "hsla(300,47%,75%,100%)", - "hwb(300,63%,13%)", - "hwb(300,63%,13%,1)", - "hwb(300,63%,13%,100%)", - ], - }, - "powderblue": { - "hex": [ - "#b0e0e6", - "#ffb0e0e6", - ], - "func": [ - "rgb(176,224,230)", - "rgba(176,224,230,1)", - "rgba(176,224,230,100%)", - "rgb(69%,88%,90%)", - "rgba(69%,88%,90%,1)", - "rgba(69%,88%,90%,100%)", - "hsl(187,52%,80%)", - "hsla(187,52%,80%,1)", - "hsla(187,52%,80%,100%)", - "hwb(187,69%,10%)", - "hwb(187,69%,10%,1)", - "hwb(187,69%,10%,100%)", - ], - }, - "purple": { - "hex": [ - "#800080", - "#ff800080", - ], - "func": [ - "rgb(128,0,128)", - "rgba(128,0,128,1)", - "rgba(128,0,128,100%)", - "rgb(50%,0%,50%)", - "rgba(50%,0%,50%,1)", - "rgba(50%,0%,50%,100%)", - "hsl(300,100%,25%)", - "hsla(300,100%,25%,1)", - "hsla(300,100%,25%,100%)", - "hwb(300,0%,50%)", - "hwb(300,0%,50%,1)", - "hwb(300,0%,50%,100%)", - ], - }, - "rebeccapurple": { - "hex": [ - "#663399", - "#ff663399", - "#639", - "#f639", - ], - "func": [ - "rgb(102,51,153)", - "rgba(102,51,153,1)", - "rgba(102,51,153,100%)", - "rgb(40%,20%,60%)", - "rgba(40%,20%,60%,1)", - "rgba(40%,20%,60%,100%)", - "hsl(270,50%,40%)", - "hsla(270,50%,40%,1)", - "hsla(270,50%,40%,100%)", - "hwb(270,20%,40%)", - "hwb(270,20%,40%,1)", - "hwb(270,20%,40%,100%)", - ], - }, - "red": { - "hex": [ - "#ff0000", - "#ffff0000", - "#f00", - "#ff00", - ], - "func": [ - "rgb(255,0,0)", - "rgba(255,0,0,1)", - "rgba(255,0,0,100%)", - "rgb(100%,0%,0%)", - "rgba(100%,0%,0%,1)", - "rgba(100%,0%,0%,100%)", - "hsl(0,100%,50%)", - "hsla(0,100%,50%,1)", - "hsla(0,100%,50%,100%)", - "hwb(0,0%,0%)", - "hwb(0,0%,0%,1)", - "hwb(0,0%,0%,100%)", - ], - }, - "rosybrown": { - "hex": [ - "#bc8f8f", - "#ffbc8f8f", - ], - "func": [ - "rgb(188,143,143)", - "rgba(188,143,143,1)", - "rgba(188,143,143,100%)", - "rgb(74%,56%,56%)", - "rgba(74%,56%,56%,1)", - "rgba(74%,56%,56%,100%)", - "hsl(0,25%,65%)", - "hsla(0,25%,65%,1)", - "hsla(0,25%,65%,100%)", - "hwb(0,56%,26%)", - "hwb(0,56%,26%,1)", - "hwb(0,56%,26%,100%)", - ], - }, - "royalblue": { - "hex": [ - "#4169e1", - "#ff4169e1", - ], - "func": [ - "rgb(65,105,225)", - "rgba(65,105,225,1)", - "rgba(65,105,225,100%)", - "rgb(25%,41%,88%)", - "rgba(25%,41%,88%,1)", - "rgba(25%,41%,88%,100%)", - "hsl(225,73%,57%)", - "hsla(225,73%,57%,1)", - "hsla(225,73%,57%,100%)", - "hwb(225,25%,12%)", - "hwb(225,25%,12%,1)", - "hwb(225,25%,12%,100%)", - ], - }, - "saddlebrown": { - "hex": [ - "#8b4513", - "#ff8b4513", - ], - "func": [ - "rgb(139,69,19)", - "rgba(139,69,19,1)", - "rgba(139,69,19,100%)", - "rgb(55%,27%,7%)", - "rgba(55%,27%,7%,1)", - "rgba(55%,27%,7%,100%)", - "hsl(25,76%,31%)", - "hsla(25,76%,31%,1)", - "hsla(25,76%,31%,100%)", - "hwb(25,7%,45%)", - "hwb(25,7%,45%,1)", - "hwb(25,7%,45%,100%)", - ], - }, - "salmon": { - "hex": [ - "#fa8072", - "#fffa8072", - ], - "func": [ - "rgb(250,128,114)", - "rgba(250,128,114,1)", - "rgba(250,128,114,100%)", - "rgb(98%,50%,45%)", - "rgba(98%,50%,45%,1)", - "rgba(98%,50%,45%,100%)", - "hsl(6,93%,71%)", - "hsla(6,93%,71%,1)", - "hsla(6,93%,71%,100%)", - "hwb(6,45%,2%)", - "hwb(6,45%,2%,1)", - "hwb(6,45%,2%,100%)", - ], - }, - "sandybrown": { - "hex": [ - "#f4a460", - "#fff4a460", - ], - "func": [ - "rgb(244,164,96)", - "rgba(244,164,96,1)", - "rgba(244,164,96,100%)", - "rgb(96%,64%,38%)", - "rgba(96%,64%,38%,1)", - "rgba(96%,64%,38%,100%)", - "hsl(28,87%,67%)", - "hsla(28,87%,67%,1)", - "hsla(28,87%,67%,100%)", - "hwb(28,38%,4%)", - "hwb(28,38%,4%,1)", - "hwb(28,38%,4%,100%)", - ], - }, - "seagreen": { - "hex": [ - "#2e8b57", - "#ff2e8b57", - ], - "func": [ - "rgb(46,139,87)", - "rgba(46,139,87,1)", - "rgba(46,139,87,100%)", - "rgb(18%,55%,34%)", - "rgba(18%,55%,34%,1)", - "rgba(18%,55%,34%,100%)", - "hsl(146,50%,36%)", - "hsla(146,50%,36%,1)", - "hsla(146,50%,36%,100%)", - "hwb(146,18%,45%)", - "hwb(146,18%,45%,1)", - "hwb(146,18%,45%,100%)", - ], - }, - "seashell": { - "hex": [ - "#fff5ee", - "#fffff5ee", - ], - "func": [ - "rgb(255,245,238)", - "rgba(255,245,238,1)", - "rgba(255,245,238,100%)", - "rgb(100%,96%,93%)", - "rgba(100%,96%,93%,1)", - "rgba(100%,96%,93%,100%)", - "hsl(25,100%,97%)", - "hsla(25,100%,97%,1)", - "hsla(25,100%,97%,100%)", - "hwb(25,93%,0%)", - "hwb(25,93%,0%,1)", - "hwb(25,93%,0%,100%)", - ], - }, - "sienna": { - "hex": [ - "#a0522d", - "#ffa0522d", - ], - "func": [ - "rgb(160,82,45)", - "rgba(160,82,45,1)", - "rgba(160,82,45,100%)", - "rgb(63%,32%,18%)", - "rgba(63%,32%,18%,1)", - "rgba(63%,32%,18%,100%)", - "hsl(19,56%,40%)", - "hsla(19,56%,40%,1)", - "hsla(19,56%,40%,100%)", - "hwb(19,18%,37%)", - "hwb(19,18%,37%,1)", - "hwb(19,18%,37%,100%)", - ], - }, - "silver": { - "hex": [ - "#c0c0c0", - "#ffc0c0c0", - ], - "func": [ - "rgb(192,192,192)", - "rgba(192,192,192,1)", - "rgba(192,192,192,100%)", - "rgb(75%,75%,75%)", - "rgba(75%,75%,75%,1)", - "rgba(75%,75%,75%,100%)", - "hsl(0,0%,75%)", - "hsla(0,0%,75%,1)", - "hsla(0,0%,75%,100%)", - "hwb(0,75%,25%)", - "hwb(0,75%,25%,1)", - "hwb(0,75%,25%,100%)", - ], - }, - "skyblue": { - "hex": [ - "#87ceeb", - "#ff87ceeb", - ], - "func": [ - "rgb(135,206,235)", - "rgba(135,206,235,1)", - "rgba(135,206,235,100%)", - "rgb(53%,81%,92%)", - "rgba(53%,81%,92%,1)", - "rgba(53%,81%,92%,100%)", - "hsl(197,71%,73%)", - "hsla(197,71%,73%,1)", - "hsla(197,71%,73%,100%)", - "hwb(197,53%,8%)", - "hwb(197,53%,8%,1)", - "hwb(197,53%,8%,100%)", - ], - }, - "slateblue": { - "hex": [ - "#6a5acd", - "#ff6a5acd", - ], - "func": [ - "rgb(106,90,205)", - "rgba(106,90,205,1)", - "rgba(106,90,205,100%)", - "rgb(42%,35%,80%)", - "rgba(42%,35%,80%,1)", - "rgba(42%,35%,80%,100%)", - "hsl(248,53%,58%)", - "hsla(248,53%,58%,1)", - "hsla(248,53%,58%,100%)", - "hwb(248,35%,20%)", - "hwb(248,35%,20%,1)", - "hwb(248,35%,20%,100%)", - ], - }, - "slategray": { - "hex": [ - "#708090", - "#ff708090", - ], - "func": [ - "rgb(112,128,144)", - "rgba(112,128,144,1)", - "rgba(112,128,144,100%)", - "rgb(44%,50%,56%)", - "rgba(44%,50%,56%,1)", - "rgba(44%,50%,56%,100%)", - "hsl(210,13%,50%)", - "hsla(210,13%,50%,1)", - "hsla(210,13%,50%,100%)", - "hwb(210,44%,44%)", - "hwb(210,44%,44%,1)", - "hwb(210,44%,44%,100%)", - ], - }, - "slategrey": { - "hex": [ - "#708090", - "#ff708090", - ], - "func": [ - "rgb(112,128,144)", - "rgba(112,128,144,1)", - "rgba(112,128,144,100%)", - "rgb(44%,50%,56%)", - "rgba(44%,50%,56%,1)", - "rgba(44%,50%,56%,100%)", - "hsl(210,13%,50%)", - "hsla(210,13%,50%,1)", - "hsla(210,13%,50%,100%)", - "hwb(210,44%,44%)", - "hwb(210,44%,44%,1)", - "hwb(210,44%,44%,100%)", - ], - }, - "snow": { - "hex": [ - "#fffafa", - "#fffffafa", - ], - "func": [ - "rgb(255,250,250)", - "rgba(255,250,250,1)", - "rgba(255,250,250,100%)", - "rgb(100%,98%,98%)", - "rgba(100%,98%,98%,1)", - "rgba(100%,98%,98%,100%)", - "hsl(0,100%,99%)", - "hsla(0,100%,99%,1)", - "hsla(0,100%,99%,100%)", - "hwb(0,98%,0%)", - "hwb(0,98%,0%,1)", - "hwb(0,98%,0%,100%)", - ], - }, - "springgreen": { - "hex": [ - "#00ff7f", - "#ff00ff7f", - ], - "func": [ - "rgb(0,255,127)", - "rgba(0,255,127,1)", - "rgba(0,255,127,100%)", - "rgb(0%,100%,50%)", - "rgba(0%,100%,50%,1)", - "rgba(0%,100%,50%,100%)", - "hsl(150,100%,50%)", - "hsla(150,100%,50%,1)", - "hsla(150,100%,50%,100%)", - "hwb(150,0%,0%)", - "hwb(150,0%,0%,1)", - "hwb(150,0%,0%,100%)", - ], - }, - "steelblue": { - "hex": [ - "#4682b4", - "#ff4682b4", - ], - "func": [ - "rgb(70,130,180)", - "rgba(70,130,180,1)", - "rgba(70,130,180,100%)", - "rgb(27%,51%,71%)", - "rgba(27%,51%,71%,1)", - "rgba(27%,51%,71%,100%)", - "hsl(207,44%,49%)", - "hsla(207,44%,49%,1)", - "hsla(207,44%,49%,100%)", - "hwb(207,27%,29%)", - "hwb(207,27%,29%,1)", - "hwb(207,27%,29%,100%)", - ], - }, - "tan": { - "hex": [ - "#d2b48c", - "#ffd2b48c", - ], - "func": [ - "rgb(210,180,140)", - "rgba(210,180,140,1)", - "rgba(210,180,140,100%)", - "rgb(82%,71%,55%)", - "rgba(82%,71%,55%,1)", - "rgba(82%,71%,55%,100%)", - "hsl(34,44%,69%)", - "hsla(34,44%,69%,1)", - "hsla(34,44%,69%,100%)", - "hwb(34,55%,18%)", - "hwb(34,55%,18%,1)", - "hwb(34,55%,18%,100%)", - ], - }, - "teal": { - "hex": [ - "#008080", - "#ff008080", - ], - "func": [ - "rgb(0,128,128)", - "rgba(0,128,128,1)", - "rgba(0,128,128,100%)", - "rgb(0%,50%,50%)", - "rgba(0%,50%,50%,1)", - "rgba(0%,50%,50%,100%)", - "hsl(180,100%,25%)", - "hsla(180,100%,25%,1)", - "hsla(180,100%,25%,100%)", - "hwb(180,0%,50%)", - "hwb(180,0%,50%,1)", - "hwb(180,0%,50%,100%)", - ], - }, - "thistle": { - "hex": [ - "#d8bfd8", - "#ffd8bfd8", - ], - "func": [ - "rgb(216,191,216)", - "rgba(216,191,216,1)", - "rgba(216,191,216,100%)", - "rgb(85%,75%,85%)", - "rgba(85%,75%,85%,1)", - "rgba(85%,75%,85%,100%)", - "hsl(300,24%,80%)", - "hsla(300,24%,80%,1)", - "hsla(300,24%,80%,100%)", - "hwb(300,75%,15%)", - "hwb(300,75%,15%,1)", - "hwb(300,75%,15%,100%)", - ], - }, - "tomato": { - "hex": [ - "#ff6347", - "#ffff6347", - ], - "func": [ - "rgb(255,99,71)", - "rgba(255,99,71,1)", - "rgba(255,99,71,100%)", - "rgb(100%,39%,28%)", - "rgba(100%,39%,28%,1)", - "rgba(100%,39%,28%,100%)", - "hsl(9,100%,64%)", - "hsla(9,100%,64%,1)", - "hsla(9,100%,64%,100%)", - "hwb(9,28%,0%)", - "hwb(9,28%,0%,1)", - "hwb(9,28%,0%,100%)", - ], - }, - "turquoise": { - "hex": [ - "#40e0d0", - "#ff40e0d0", - ], - "func": [ - "rgb(64,224,208)", - "rgba(64,224,208,1)", - "rgba(64,224,208,100%)", - "rgb(25%,88%,82%)", - "rgba(25%,88%,82%,1)", - "rgba(25%,88%,82%,100%)", - "hsl(174,72%,56%)", - "hsla(174,72%,56%,1)", - "hsla(174,72%,56%,100%)", - "hwb(174,25%,12%)", - "hwb(174,25%,12%,1)", - "hwb(174,25%,12%,100%)", - ], - }, - "violet": { - "hex": [ - "#ee82ee", - "#ffee82ee", - ], - "func": [ - "rgb(238,130,238)", - "rgba(238,130,238,1)", - "rgba(238,130,238,100%)", - "rgb(93%,51%,93%)", - "rgba(93%,51%,93%,1)", - "rgba(93%,51%,93%,100%)", - "hsl(300,76%,72%)", - "hsla(300,76%,72%,1)", - "hsla(300,76%,72%,100%)", - "hwb(300,51%,7%)", - "hwb(300,51%,7%,1)", - "hwb(300,51%,7%,100%)", - ], - }, - "wheat": { - "hex": [ - "#f5deb3", - "#fff5deb3", - ], - "func": [ - "rgb(245,222,179)", - "rgba(245,222,179,1)", - "rgba(245,222,179,100%)", - "rgb(96%,87%,70%)", - "rgba(96%,87%,70%,1)", - "rgba(96%,87%,70%,100%)", - "hsl(39,77%,83%)", - "hsla(39,77%,83%,1)", - "hsla(39,77%,83%,100%)", - "hwb(39,70%,4%)", - "hwb(39,70%,4%,1)", - "hwb(39,70%,4%,100%)", - ], - }, - "white": { - "hex": [ - "#ffffff", - "#ffffffff", - "#fff", - "#ffff", - ], - "func": [ - "rgb(255,255,255)", - "rgba(255,255,255,1)", - "rgba(255,255,255,100%)", - "rgb(100%,100%,100%)", - "rgba(100%,100%,100%,1)", - "rgba(100%,100%,100%,100%)", - "hsl(0,0%,100%)", - "hsla(0,0%,100%,1)", - "hsla(0,0%,100%,100%)", - "hwb(0,100%,0%)", - "hwb(0,100%,0%,1)", - "hwb(0,100%,0%,100%)", - "gray(255)", - "gray(255,1)", - "gray(255,100%)", - "gray(255%)", - "gray(255%,1)", - "gray(255%,100%)", - ], - }, - "whitesmoke": { - "hex": [ - "#f5f5f5", - "#fff5f5f5", - ], - "func": [ - "rgb(245,245,245)", - "rgba(245,245,245,1)", - "rgba(245,245,245,100%)", - "rgb(96%,96%,96%)", - "rgba(96%,96%,96%,1)", - "rgba(96%,96%,96%,100%)", - "hsl(0,0%,96%)", - "hsla(0,0%,96%,1)", - "hsla(0,0%,96%,100%)", - "hwb(0,96%,4%)", - "hwb(0,96%,4%,1)", - "hwb(0,96%,4%,100%)", - "gray(245)", - "gray(245,1)", - "gray(245,100%)", - "gray(245%)", - "gray(245%,1)", - "gray(245%,100%)", - ], - }, - "yellow": { - "hex": [ - "#ffff00", - "#ffffff00", - "#ff0", - "#fff0", - ], - "func": [ - "rgb(255,255,0)", - "rgba(255,255,0,1)", - "rgba(255,255,0,100%)", - "rgb(100%,100%,0%)", - "rgba(100%,100%,0%,1)", - "rgba(100%,100%,0%,100%)", - "hsl(60,100%,50%)", - "hsla(60,100%,50%,1)", - "hsla(60,100%,50%,100%)", - "hwb(60,0%,0%)", - "hwb(60,0%,0%,1)", - "hwb(60,0%,0%,100%)", - ], - }, - "yellowgreen": { - "hex": [ - "#9acd32", - "#ff9acd32", - ], - "func": [ - "rgb(154,205,50)", - "rgba(154,205,50,1)", - "rgba(154,205,50,100%)", - "rgb(60%,80%,20%)", - "rgba(60%,80%,20%,1)", - "rgba(60%,80%,20%,100%)", - "hsl(80,61%,50%)", - "hsla(80,61%,50%,1)", - "hsla(80,61%,50%,100%)", - "hwb(80,20%,20%)", - "hwb(80,20%,20%,1)", - "hwb(80,20%,20%,100%)", - ], - }, -} diff --git a/src/reference/propertySets.js b/src/reference/propertySets.js deleted file mode 100644 index ae8f4a2f3a..0000000000 --- a/src/reference/propertySets.js +++ /dev/null @@ -1,12 +0,0 @@ -export const acceptCustomIdents = new Set([ - "animation", - "animation-name", - "font", - "font-family", - "counter-increment", - "grid-row", - "grid-column", - "grid-area", - "list-style", - "list-style-type", -]) diff --git a/src/reference/punctuationSets.js b/src/reference/punctuationSets.js deleted file mode 100644 index 36a929c2e4..0000000000 --- a/src/reference/punctuationSets.js +++ /dev/null @@ -1,14 +0,0 @@ -export const mediaFeaturePunctuation = new Set([ - ":", - "=", - ">", - ">=", - "<", - "<=", -]) - -export const nonSpaceCombinators = new Set([ - ">", - "+", - "~", -]) diff --git a/src/reference/shorthandData.js b/src/reference/shorthandData.js deleted file mode 100644 index f0e3130076..0000000000 --- a/src/reference/shorthandData.js +++ /dev/null @@ -1,120 +0,0 @@ -export default { - "margin": [ - "margin-top", - "margin-bottom", - "margin-left", - "margin-right", - ], - "padding": [ - "padding-top", - "padding-bottom", - "padding-left", - "padding-right", - ], - "background": [ - "background-image", - "background-size", - "background-position", - "background-repeat", - "background-origin", - "background-clip", - "background-attachment", - "background-color", - ], - "font": [ - "font-style", - "font-variant", - "font-weight", - "font-stretch", - "font-size", - "font-family", - "line-height", - ], - "border": [ - "border-top-width", - "border-bottom-width", - "border-left-width", - "border-right-width", - "border-top-style", - "border-bottom-style", - "border-left-style", - "border-right-style", - "border-top-color", - "border-bottom-color", - "border-left-color", - "border-right-color", - ], - "border-top": [ - "border-top-width", - "border-top-style", - "border-top-color", - ], - "border-bottom": [ - "border-bottom-width", - "border-bottom-style", - "border-bottom-color", - ], - "border-left": [ - "border-left-width", - "border-left-style", - "border-left-color", - ], - "border-right": [ - "border-right-width", - "border-right-style", - "border-right-color", - ], - "border-width": [ - "border-top-width", - "border-bottom-width", - "border-left-width", - "border-right-width", - ], - "border-style": [ - "border-top-style", - "border-bottom-style", - "border-left-style", - "border-right-style", - ], - "border-color": [ - "border-top-color", - "border-bottom-color", - "border-left-color", - "border-right-color", - ], - "list-style": [ - "list-style-type", - "list-style-position", - "list-style-image", - ], - "border-radius": [ - "border-top-right-radius", - "border-top-left-radius", - "border-bottom-right-radius", - "border-bottom-left-radius", - ], - "transition": [ - "transition-delay", - "transition-duration", - "transition-property", - "transition-timing-function", - ], - "-webkit-transition": [ - "-webkit-transition-delay", - "-webkit-transition-duration", - "-webkit-transition-property", - "-webkit-transition-timing-function", - ], - "-moz-transition": [ - "-moz-transition-delay", - "-moz-transition-duration", - "-moz-transition-property", - "-moz-transition-timing-function", - ], - "-o-transition": [ - "-o-transition-delay", - "-o-transition-duration", - "-o-transition-property", - "-o-transition-timing-function", - ], -} diff --git a/src/rules/at-rule-blacklist/index.js b/src/rules/at-rule-blacklist/index.js deleted file mode 100644 index 6c214e8470..0000000000 --- a/src/rules/at-rule-blacklist/index.js +++ /dev/null @@ -1,41 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import { vendor } from "postcss" - -export const ruleName = "at-rule-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (name) => `Unexpected at-rule "${name}"`, -}) - -function rule(blacklistInput) { - // To allow for just a string as a parameter (not only arrays of strings) - const blacklist = [].concat(blacklistInput) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkAtRules(atRule => { - const { name } = atRule - if (blacklist.indexOf(vendor.unprefixed(name).toLowerCase()) === -1) { return } - - report({ - message: messages.rejected(name), - node: atRule, - result, - ruleName, - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/at-rule-empty-line-before/index.js b/src/rules/at-rule-empty-line-before/index.js deleted file mode 100644 index 4207f7059f..0000000000 --- a/src/rules/at-rule-empty-line-before/index.js +++ /dev/null @@ -1,132 +0,0 @@ -import { - hasBlock, - hasEmptyLine, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" - -export const ruleName = "at-rule-empty-line-before" - -export const messages = ruleMessages(ruleName, { - expected: "Expected empty line before at-rule", - rejected: "Unexpected empty line before at-rule", -}) - -export default function (expectation, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ "always", "never" ], - }, { - actual: options, - possible: { - except: [ - "all-nested", - "blockless-after-same-name-blockless", - "blockless-group", - "first-nested", - ], - ignore: [ - "after-comment", - "all-nested", - "blockless-after-same-name-blockless", - "blockless-group", - ], - ignoreAtRules: [isString], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkAtRules(atRule => { - // Ignore the first node - if (atRule === root.first) { return } - - // Return early if at-rule is to be ignored - if (optionsMatches(options, "ignoreAtRules", atRule.name)) { return } - - // Optionally ignore the expectation if the node is blockless - if (optionsMatches(options, "ignore", "blockless-group") - && !hasBlock(atRule)) { return } - - const isNested = atRule.parent !== root - const previousNode = atRule.prev() - - // Optionally ignore the expection if the node is blockless - // and following another blockless at-rule with the same name - if (optionsMatches(options, "ignore", "blockless-after-same-name-blockless") - && isBlocklessAfterSameNameBlockless()) { return } - - // Optionally ignore the expectation if the node is nested - if (optionsMatches(options, "ignore", "all-nested") - && isNested) { return } - - // Optionally ignore the expectation if a comment precedes this node - if (optionsMatches(options, "ignore", "after-comment") - && isAfterComment()) { return } - - const hasEmptyLineBefore = hasEmptyLine(atRule.raws.before) - let expectEmptyLineBefore = (expectation === "always") ? true : false - - // Optionally reverse the expectation if any exceptions apply - if ( - ((optionsMatches(options, "except", "all-nested") - && isNested)) - || (optionsMatches(options, "except", "first-nested") - && isFirstNested()) - || (optionsMatches(options, "except", "blockless-group") - && isBlocklessAfterBlockless()) - || (optionsMatches(options, "except", "blockless-after-same-name-blockless") - && isBlocklessAfterSameNameBlockless()) - ) { - expectEmptyLineBefore = !expectEmptyLineBefore - } - - // Return if the expectation is met - if (expectEmptyLineBefore === hasEmptyLineBefore) { return } - - const message = expectEmptyLineBefore ? messages.expected : messages.rejected - - report({ - message, - node: atRule, - result, - ruleName, - }) - - function isAfterComment() { - return ( - previousNode - && previousNode.type === "comment" - ) - } - - function isBlocklessAfterBlockless() { - return ( - previousNode && previousNode.type === "atrule" - && !hasBlock(previousNode) - && !hasBlock(atRule) - ) - } - - function isBlocklessAfterSameNameBlockless() { - return ( - !hasBlock(atRule) - && previousNode && !hasBlock(previousNode) - && previousNode.type === "atrule" - && previousNode.name == atRule.name - ) - } - - function isFirstNested() { - return ( - isNested - && atRule === atRule.parent.first - ) - } - }) - } -} diff --git a/src/rules/at-rule-name-case/index.js b/src/rules/at-rule-name-case/index.js deleted file mode 100644 index 19193f4ca4..0000000000 --- a/src/rules/at-rule-name-case/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "at-rule-name-case" - -export const messages = ruleMessages(ruleName, { - expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "lower", - "upper", - ], - }) - if (!validOptions) { return } - - root.walkAtRules(atRule => { - const { name } = atRule - const expectedName = expectation === "lower" ? name.toLowerCase() : name.toUpperCase() - - if (name === expectedName) { return } - - report({ - message: messages.expected(name, expectedName), - node: atRule, - ruleName, - result, - }) - }) - } -} diff --git a/src/rules/at-rule-name-newline-after/index.js b/src/rules/at-rule-name-newline-after/index.js deleted file mode 100644 index 32ffd1bbc6..0000000000 --- a/src/rules/at-rule-name-newline-after/index.js +++ /dev/null @@ -1,33 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { atRuleNameSpaceChecker } from "../at-rule-name-space-after" - -export const ruleName = "at-rule-name-newline-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: (name) => `Expected newline after at-rule name \"${name}\"`, -}) - -export default function (expectation) { - const checker = whitespaceChecker("newline", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "always-multi-line", - ], - }) - if (!validOptions) { return } - - atRuleNameSpaceChecker({ - root, - result, - locationChecker: checker.afterOneOnly, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/at-rule-name-space-after/index.js b/src/rules/at-rule-name-space-after/index.js deleted file mode 100644 index 75839cbdcd..0000000000 --- a/src/rules/at-rule-name-space-after/index.js +++ /dev/null @@ -1,61 +0,0 @@ -import { - isStandardSyntaxAtRule, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -export const ruleName = "at-rule-name-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: (name) => `Expected single space after at-rule name \"${name}\"`, -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "always-single-line", - ], - }) - if (!validOptions) { return } - - atRuleNameSpaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - }) - } -} - -export function atRuleNameSpaceChecker({ locationChecker, root, result, checkedRuleName }) { - root.walkAtRules(atRule => { - if (!isStandardSyntaxAtRule(atRule)) { return } - - checkColon( - `@${atRule.name}${atRule.raws.afterName}${atRule.params}`, - atRule.name.length, - atRule - ) - }) - - function checkColon(source, index, node) { - locationChecker({ - source, - index, - err: m => report({ - message: m, - node, - index, - result, - ruleName: checkedRuleName, - }), - errTarget: `@${node.name}`, - }) - } -} diff --git a/src/rules/at-rule-no-unknown/index.js b/src/rules/at-rule-no-unknown/index.js deleted file mode 100644 index 1bfd5a73ad..0000000000 --- a/src/rules/at-rule-no-unknown/index.js +++ /dev/null @@ -1,45 +0,0 @@ -import { - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { atRules } from "../../reference/keywordSets" -import { isString } from "lodash" -import { vendor } from "postcss" - -export const ruleName = "at-rule-no-unknown" - -export const messages = ruleMessages(ruleName, { - rejected: (atRule) => `Unexpected unknown at-rule "${atRule}"`, -}) - -export default function (actual, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }, { - actual: options, - possible: { - ignoreAtRules: [isString], - }, - optional: true, - }) - - if (!validOptions) { return } - - root.walkAtRules(atRule => { - const { name } = atRule - - // Return early if at-rule is to be ignored - if (optionsMatches(options, "ignoreAtRules", atRule.name)) { return } - - if (vendor.prefix(name) || atRules.has(name.toLowerCase())) { return } - - report({ - message: messages.rejected(`@${name}`), - node: atRule, - ruleName, - result, - }) - }) - } -} diff --git a/src/rules/at-rule-no-vendor-prefix/index.js b/src/rules/at-rule-no-vendor-prefix/index.js deleted file mode 100644 index dd2195bf1e..0000000000 --- a/src/rules/at-rule-no-vendor-prefix/index.js +++ /dev/null @@ -1,37 +0,0 @@ -import { - isAutoprefixable, - isStandardSyntaxAtRule, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "at-rule-no-vendor-prefix" - -export const messages = ruleMessages(ruleName, { - rejected: p => `Unexpected vendor-prefixed at-rule "@${p}"`, -}) - -export default function (actual) { - return function (root, result) { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkAtRules(atRule => { - if (!isStandardSyntaxAtRule(atRule)) { return } - - const { name } = atRule - - if (name[0] !== "-") { return } - - if (!isAutoprefixable.atRuleName(name)) { return } - - report({ - message: messages.rejected(name), - node: atRule, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/at-rule-semicolon-newline-after/index.js b/src/rules/at-rule-semicolon-newline-after/index.js deleted file mode 100644 index b33c6220b7..0000000000 --- a/src/rules/at-rule-semicolon-newline-after/index.js +++ /dev/null @@ -1,51 +0,0 @@ -import { - hasBlock, - nextNonCommentNode, - rawNodeString, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -export const ruleName = "at-rule-semicolon-newline-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected newline after \";\"", -}) - -export default function (actual) { - const checker = whitespaceChecker("newline", actual, messages) - - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual, - possible: ["always"], - }) - if (!validOptions) { return } - - root.walkAtRules(atRule => { - const nextNode = atRule.next() - if (!nextNode) { return } - if (hasBlock(atRule)) { return } - - // Allow an end-of-line comment - const nodeToCheck = nextNonCommentNode(nextNode) - if (!nodeToCheck) { return } - - checker.afterOneOnly({ - source: rawNodeString(nodeToCheck), - index: -1, - err: msg => { - report({ - message: msg, - node: atRule, - index: atRule.toString().length + 1, - result, - ruleName, - }) - }, - }) - }) - } -} diff --git a/src/rules/at-rule-whitelist/index.js b/src/rules/at-rule-whitelist/index.js deleted file mode 100644 index d38448d2c2..0000000000 --- a/src/rules/at-rule-whitelist/index.js +++ /dev/null @@ -1,41 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import { vendor } from "postcss" - -export const ruleName = "at-rule-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (name) => `Unexpected at-rule "${name}"`, -}) - -function rule(whitelistInput) { - // To allow for just a string as a parameter (not only arrays of strings) - const whitelist = [].concat(whitelistInput) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkAtRules(atRule => { - const { name } = atRule - if (whitelist.indexOf(vendor.unprefixed(name).toLowerCase()) !== -1) { return } - - report({ - message: messages.rejected(name), - node: atRule, - result, - ruleName, - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/block-closing-brace-empty-line-before/index.js b/src/rules/block-closing-brace-empty-line-before/index.js deleted file mode 100644 index a8cf9f7569..0000000000 --- a/src/rules/block-closing-brace-empty-line-before/index.js +++ /dev/null @@ -1,69 +0,0 @@ -import { - blockString, - hasBlock, - hasEmptyBlock, - hasEmptyLine, - isSingleLineString, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "block-closing-brace-empty-line-before" - -export const messages = ruleMessages(ruleName, { - expected: "Expected empty line before closing brace", - rejected: "Unexpected empty line before closing brace", -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always-multi-line", - "never", - ], - }) - if (!validOptions) { return } - - // Check both kinds of statements: rules and at-rules - root.walkRules(check) - root.walkAtRules(check) - - function check(statement) { - // Return early if blockless or has empty block - if (!hasBlock(statement) || hasEmptyBlock(statement)) { return } - - // Get whitespace after ""}", ignoring extra semicolon - const before = (statement.raws.after || "").replace(/;+/, "") - if (before === undefined) { return } - - // Calculate index - const statementString = statement.toString() - let index = statementString.length - 1 - if (statementString[index - 1] === "\r") { index -= 1 } - - // Set expectation - const expectEmptyLineBefore = ( - expectation === "always-multi-line" - && !isSingleLineString(blockString(statement)) - ) ? true : false - - // Check for at least one empty line - const hasEmptyLineBefore = hasEmptyLine(before) - - // Return if the expectation is met - if (expectEmptyLineBefore === hasEmptyLineBefore) { return } - - const message = expectEmptyLineBefore ? messages.expected : messages.rejected - report({ - message, - result, - ruleName, - node: statement, - index, - }) - } - } -} diff --git a/src/rules/block-no-single-line/index.js b/src/rules/block-no-single-line/index.js deleted file mode 100644 index ac34b9f1b7..0000000000 --- a/src/rules/block-no-single-line/index.js +++ /dev/null @@ -1,40 +0,0 @@ -import { - beforeBlockString, - blockString, - hasBlock, - hasEmptyBlock, - isSingleLineString, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "block-no-single-line" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected single-line block", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - // Check both kinds of statements: rules and at-rules - root.walkRules(check) - root.walkAtRules(check) - - function check(statement) { - if (!hasBlock(statement) || hasEmptyBlock(statement)) { return } - if (!isSingleLineString(blockString(statement))) { return } - - report({ - message: messages.rejected, - node: statement, - index: beforeBlockString(statement, { noRawBefore: true }).length, - result, - ruleName, - }) - } - } -} diff --git a/src/rules/color-named/index.js b/src/rules/color-named/index.js deleted file mode 100644 index 2f57452067..0000000000 --- a/src/rules/color-named/index.js +++ /dev/null @@ -1,130 +0,0 @@ -import { - declarationValueIndex, - isStandardSyntaxValue, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { acceptCustomIdents } from "../../reference/propertySets" -import { colorFunctionNames } from "../../reference/keywordSets" -import { isString } from "lodash" -import namedColorData from "../../reference/namedColorData" -import valueParser from "postcss-value-parser" - -export const ruleName = "color-named" - -export const messages = ruleMessages(ruleName, { - expected: (named, original) => ( - `Expected "${original}" to be "${named}"` - ), - rejected: (named) => ( - `Unexpected named color "${named}"` - ), -}) - -// Todo tested on case insensivity -const NODE_TYPES = [ "word", "function" ] - -export default function (expectation, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "never", - "always-where-possible", - ], - }, { - actual: options, - possible: { - ignoreProperties: [isString], - ignore: ["inside-function"], - }, - optional: true, - }) - - if (!validOptions) { return } - - const namedColors = Object.keys(namedColorData) - - root.walkDecls(decl => { - if (acceptCustomIdents.has(decl.prop)) { return } - - // Return early if the property is to be ignored - if (optionsMatches(options, "ignoreProperties", decl.prop)) { return } - - valueParser(decl.value).walk(node => { - const { value, type, sourceIndex } = node - - if (optionsMatches(options, "ignore", "inside-function") && type === "function") { return false } - - if (!isStandardSyntaxValue(value)) { return } - // Return early if neither a word nor a function - if (NODE_TYPES.indexOf(type) === -1) { return } - - // Check for named colors for "never" option - if ( - expectation === "never" - && type === "word" - && namedColors.indexOf(value.toLowerCase()) !== -1 - ) { - complain( - messages.rejected(value), - decl, - declarationValueIndex(decl) + sourceIndex - ) - return - } - - // Check "always-where-possible" option ... - if (expectation !== "always-where-possible") { return } - - // First by checking for alternative color function representations ... - if ( - type === "function" - && colorFunctionNames.has(value.toLowerCase()) - ) { - // Remove all spaces to match what's in `representations` - const normalizedFunctionString = valueParser.stringify(node).replace(/\s+/g, "") - let namedColor - for (let i = 0, l = namedColors.length; i < l; i++) { - namedColor = namedColors[i] - if (namedColorData[namedColor].func.indexOf(normalizedFunctionString.toLowerCase()) !== -1) { - complain( - messages.expected(namedColor, normalizedFunctionString), - decl, - declarationValueIndex(decl) + sourceIndex - ) - return // Exit as soon as a problem is found - } - } - return - } - - // Then by checking for alternative hex representations - let namedColor - for (let i = 0, l = namedColors.length; i < l; i++) { - namedColor = namedColors[i] - if (namedColorData[namedColor].hex.indexOf(value.toLowerCase()) !== -1) { - complain( - messages.expected(namedColor, value), - decl, - declarationValueIndex(decl) + sourceIndex - ) - return // Exit as soon as a problem is found - } - } - }) - }) - - function complain(message, node, index) { - report({ - result, - ruleName, - message, - node, - index, - }) - } - } -} diff --git a/src/rules/comment-empty-line-before/index.js b/src/rules/comment-empty-line-before/index.js deleted file mode 100644 index b91915a778..0000000000 --- a/src/rules/comment-empty-line-before/index.js +++ /dev/null @@ -1,87 +0,0 @@ -import { - hasEmptyLine, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "comment-empty-line-before" - -export const messages = ruleMessages(ruleName, { - expected: "Expected empty line before comment", - rejected: "Unexpected empty line before comment", -}) - -const stylelintCommandPrefix = "stylelint-" - -export default function (expectation, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }, { - actual: options, - possible: { - except: ["first-nested"], - ignore: [ - "stylelint-commands", - "between-comments", - ], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkComments(comment => { - // Ignore the first node - if (comment === root.first) { return } - - // Optionally ignore stylelint commands - if ( - comment.text.indexOf(stylelintCommandPrefix) === 0 - && optionsMatches(options, "ignore", "stylelint-commands") - ) { return } - - // Optionally ignore newlines between comments - const prev = comment.prev() - if ( - prev && prev.type === "comment" - && optionsMatches(options, "ignore", "between-comments") - ) { return } - - if (comment.raws.inline || comment.inline) { return } - - const before = comment.raws.before - - // Ignore shared-line comments - if (before.indexOf("\n") === -1) { return } - - const expectEmptyLineBefore = (() => { - if ( - optionsMatches(options, "except", "first-nested") - && comment.parent !== root - && comment === comment.parent.first - ) { return false } - return expectation === "always" - })() - - const hasEmptyLineBefore = hasEmptyLine(before) - - // Return if the expectation is met - if (expectEmptyLineBefore === hasEmptyLineBefore) { return } - - const message = expectEmptyLineBefore ? messages.expected : messages.rejected - - report({ - message, - node: comment, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/comment-no-empty/index.js b/src/rules/comment-no-empty/index.js deleted file mode 100644 index 2395656456..0000000000 --- a/src/rules/comment-no-empty/index.js +++ /dev/null @@ -1,31 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "comment-no-empty" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected empty comment", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkComments(comment => { - // To ignore inline SCSS comments - if (comment.raws.inline || comment.inline) { return } - // To ignore comments that are not empty - if (comment.text && comment.text.length !== 0) { return } - report({ - message: messages.rejected, - node: comment, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/comment-word-blacklist/index.js b/src/rules/comment-word-blacklist/index.js deleted file mode 100644 index 13e922e1e4..0000000000 --- a/src/rules/comment-word-blacklist/index.js +++ /dev/null @@ -1,48 +0,0 @@ -import { - containsString, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" - -export const ruleName = "comment-word-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (pattern) => `Unexpected word matching pattern "${pattern}"`, -}) - -function rule(blacklist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkComments(comment => { - const text = comment.text - const rawComment = comment.toString() - const firstFourChars = rawComment.substr(0, 4) - - // Return early if sourcemap - if (firstFourChars === "/*# ") { return } - - const matchesWord = matchesStringOrRegExp(text, blacklist) || containsString(text, blacklist) - - if (!matchesWord) { return } - - report({ - message: messages.rejected(matchesWord.pattern), - node: comment, - result, - ruleName, - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/custom-media-pattern/index.js b/src/rules/custom-media-pattern/index.js deleted file mode 100644 index 6983e2ad59..0000000000 --- a/src/rules/custom-media-pattern/index.js +++ /dev/null @@ -1,46 +0,0 @@ -import { - atRuleParamIndex, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - isRegExp, - isString, -} from "lodash" - -export const ruleName = "custom-media-pattern" - -export const messages = ruleMessages(ruleName, { - expected: "Expected custom media query name to match specified pattern", -}) - -export default function (pattern) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: pattern, - possible: [ isRegExp, isString ], - }) - if (!validOptions) { return } - - const regexpPattern = (isString(pattern)) - ? new RegExp(pattern) - : pattern - - root.walkAtRules(atRule => { - if (atRule.name.toLowerCase() !== "custom-media") { return } - - const customMediaName = atRule.params.match(/^--(\S+)\b/)[1] - - if (regexpPattern.test(customMediaName)) { return } - - report({ - message: messages.expected, - node: atRule, - index: atRuleParamIndex(atRule), - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/custom-property-empty-line-before/index.js b/src/rules/custom-property-empty-line-before/index.js deleted file mode 100644 index b76d6b1685..0000000000 --- a/src/rules/custom-property-empty-line-before/index.js +++ /dev/null @@ -1,105 +0,0 @@ -import { - blockString, - hasEmptyLine, - isCustomProperty, - isSingleLineString, - isStandardSyntaxDeclaration, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "custom-property-empty-line-before" - -export const messages = ruleMessages(ruleName, { - expected: "Expected empty line before custom property", - rejected: "Unexpected empty line before custom property", -}) - -export default function (expectation, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }, { - actual: options, - possible: { - except: [ - "first-nested", - "after-comment", - "after-custom-property", - ], - ignore: [ - "after-comment", - "inside-single-line-block", - ], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop, parent } = decl - - if (!isStandardSyntaxDeclaration(decl)) { return } - if (!isCustomProperty(prop)) { return } - - // Optionally ignore the node if a comment precedes it - if ( - optionsMatches(options, "ignore", "after-comment") - && decl.prev() - && decl.prev().type === "comment" - ) { - return - } - - // Optionally ignore nodes inside single-line blocks - if ( - optionsMatches(options, "ignore", "inside-single-line-block") - && isSingleLineString(blockString(parent)) - ) { - return - } - - let expectEmptyLineBefore = (expectation === "always") ? true : false - - // Optionally reverse the expectation for the first nested node - if (optionsMatches(options, "except", "first-nested") - && decl === parent.first) { - expectEmptyLineBefore = !expectEmptyLineBefore - } - - // Optionally reverse the expectation if a comment precedes this node - if (optionsMatches(options, "except", "after-comment") - && decl.prev() - && decl.prev().type === "comment") { - expectEmptyLineBefore = !expectEmptyLineBefore - } - - // Optionally reverse the expectation if a custom property precedes this node - if (optionsMatches(options, "except", "after-custom-property") - && decl.prev() - && decl.prev().prop - && isCustomProperty(decl.prev().prop)) { - expectEmptyLineBefore = !expectEmptyLineBefore - } - - const hasEmptyLineBefore = hasEmptyLine(decl.raws["before"]) - - // Return if the expectation is met - if (expectEmptyLineBefore === hasEmptyLineBefore) { return } - - const message = expectEmptyLineBefore ? messages.expected : messages.rejected - report({ - message, - node: decl, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/custom-property-no-outside-root/index.js b/src/rules/custom-property-no-outside-root/index.js deleted file mode 100644 index fa576c136d..0000000000 --- a/src/rules/custom-property-no-outside-root/index.js +++ /dev/null @@ -1,34 +0,0 @@ -import { - isCustomProperty, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "custom-property-no-outside-root" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected custom property", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - // Ignore rules whose selector is just `:root` - if (rule.selector.toLowerCase().trim() === ":root") { return } - - rule.walkDecls(decl => { - if (!isCustomProperty(decl.prop)) { return } - report({ - message: messages.rejected, - node: decl, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/custom-property-pattern/index.js b/src/rules/custom-property-pattern/index.js deleted file mode 100644 index 3295a3bd64..0000000000 --- a/src/rules/custom-property-pattern/index.js +++ /dev/null @@ -1,43 +0,0 @@ -import { - isCustomProperty, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - isRegExp, - isString } -from "lodash" - -export const ruleName = "custom-property-pattern" - -export const messages = ruleMessages(ruleName, { - expected: "Expected custom property name to match specified pattern", -}) - -export default function (pattern) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: pattern, - possible: [ isRegExp, isString ], - }) - if (!validOptions) { return } - - const regexpPattern = (isString(pattern)) - ? new RegExp(pattern) - : pattern - - root.walkDecls(decl => { - const { prop } = decl - if (!isCustomProperty(prop)) { return } - if (regexpPattern.test(prop.slice(2))) { return } - - report({ - message: messages.expected, - node: decl, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/declaration-bang-space-after/index.js b/src/rules/declaration-bang-space-after/index.js deleted file mode 100644 index 9e87f8e7c9..0000000000 --- a/src/rules/declaration-bang-space-after/index.js +++ /dev/null @@ -1,61 +0,0 @@ -import { - declarationValueIndex, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "declaration-bang-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected single space after \"!\"", - rejectedAfter: () => "Unexpected whitespace after \"!\"", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - declarationBangSpaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - }) - } -} - -export function declarationBangSpaceChecker({ locationChecker, root, result, checkedRuleName }) { - root.walkDecls(function (decl) { - const indexOffset = declarationValueIndex(decl) - const declString = decl.toString() - const valueString = decl.toString().slice(indexOffset) - if (valueString.indexOf("!") == -1) { return } - - styleSearch({ source: valueString, target: "!" }, match => { - check(declString, match.startIndex + indexOffset, decl) - }) - }) - - function check(source, index, node) { - locationChecker({ source, index, err: m => - report({ - message: m, - node, - index, - result, - ruleName: checkedRuleName, - }), - }) - } -} diff --git a/src/rules/declaration-bang-space-before/index.js b/src/rules/declaration-bang-space-before/index.js deleted file mode 100644 index fc5453fbb6..0000000000 --- a/src/rules/declaration-bang-space-before/index.js +++ /dev/null @@ -1,34 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { declarationBangSpaceChecker } from "../declaration-bang-space-after" - -export const ruleName = "declaration-bang-space-before" - -export const messages = ruleMessages(ruleName, { - expectedBefore: () => "Expected single space before \"!\"", - rejectedBefore: () => "Unexpected whitespace before \"!\"", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - declarationBangSpaceChecker({ - root, - result, - locationChecker: checker.before, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/declaration-block-no-redundant-longhand-properties/index.js b/src/rules/declaration-block-no-redundant-longhand-properties/index.js deleted file mode 100644 index aae6c21f79..0000000000 --- a/src/rules/declaration-block-no-redundant-longhand-properties/index.js +++ /dev/null @@ -1,74 +0,0 @@ -import { - isEqual, - isString, - transform, -} from "lodash" -import { - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import shorthandData from "../../reference/shorthandData" - -export const ruleName = "declaration-block-no-redundant-longhand-properties" - -export const messages = ruleMessages(ruleName, { - expected: (props) => ( - `Expected shorthand property "${props}"` - ), -}) - -export default function (actual, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }, { - actual: options, - possible: { - ignoreShorthands: [isString], - }, - optional: true, - }) - if (!validOptions) { return } - - const longhandProperties = transform(shorthandData, (result, values, key) => { - if (optionsMatches(options, "ignoreShorthands", key)) { return } - - values.forEach((value) => { - (result[value] || (result[value] = [])).push(key) - }) - }) - - root.walkRules(check) - root.walkAtRules(check) - - function check(statement) { - const longhandDeclarations = {} - // Shallow iteration so nesting doesn't produce - // false positives - statement.each(node => { - if (node.type !== "decl") { return } - - const prop = node.prop.toLowerCase() - - const shorthandProperties = longhandProperties[prop] - - if (!shorthandProperties) { return } - - shorthandProperties.forEach((shorthandProperty) => { - (longhandDeclarations[shorthandProperty] || (longhandDeclarations[shorthandProperty] = [])).push(prop) - - if (!isEqual(shorthandData[shorthandProperty].sort(), longhandDeclarations[shorthandProperty].sort())) { - return - } - - report({ - ruleName, - result, - node, - message: messages.expected(shorthandProperty), - }) - }) - }) - } - } -} diff --git a/src/rules/declaration-block-no-shorthand-property-overrides/index.js b/src/rules/declaration-block-no-shorthand-property-overrides/index.js deleted file mode 100644 index 41eacae947..0000000000 --- a/src/rules/declaration-block-no-shorthand-property-overrides/index.js +++ /dev/null @@ -1,48 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import shorthandData from "../../reference/shorthandData" - -export const ruleName = "declaration-block-no-shorthand-property-overrides" - -export const messages = ruleMessages(ruleName, { - rejected: (shorthand, original) => ( - `Unexpected shorthand "${shorthand}" after "${original}"` - ), -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(check) - root.walkAtRules(check) - - function check(statement) { - const declarations = {} - // Shallow iteration so nesting doesn't produce - // false positives - statement.each(node => { - if (node.type !== "decl") { return } - const { prop } = node - const overrideables = shorthandData[prop.toLowerCase()] - if (!overrideables) { - declarations[prop.toLowerCase()] = prop - return - } - overrideables.forEach(longhandProp => { - if (!declarations.hasOwnProperty(longhandProp)) { return } - report({ - ruleName, - result, - node, - message: messages.rejected(prop, declarations[longhandProp]), - }) - }) - }) - } - } -} diff --git a/src/rules/declaration-block-single-line-max-declarations/index.js b/src/rules/declaration-block-single-line-max-declarations/index.js deleted file mode 100644 index 77663565f3..0000000000 --- a/src/rules/declaration-block-single-line-max-declarations/index.js +++ /dev/null @@ -1,42 +0,0 @@ -import { - beforeBlockString, - blockString, - isSingleLineString, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isNumber } from "lodash" - -export const ruleName = "declaration-block-single-line-max-declarations" - -export const messages = ruleMessages(ruleName, { - expected: (quantity) => `Expected no more than ${quantity} declaration(s)`, -}) - -export default function (quantity) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: quantity, - possible: [isNumber], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isSingleLineString(blockString(rule))) { return } - if (!rule.nodes) { return } - - const decls = rule.nodes.filter(node => node.type === "decl") - - if (decls.length <= quantity) { return } - - report({ - message: messages.expected(quantity), - node: rule, - index: beforeBlockString(rule, { noRawBefore: true }).length, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/declaration-block-trailing-semicolon/index.js b/src/rules/declaration-block-trailing-semicolon/index.js deleted file mode 100644 index a3d144bc1c..0000000000 --- a/src/rules/declaration-block-trailing-semicolon/index.js +++ /dev/null @@ -1,59 +0,0 @@ -import { - hasBlock, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "declaration-block-trailing-semicolon" - -export const messages = ruleMessages(ruleName, { - expected: "Expected a trailing semicolon", - rejected: "Unexpected trailing semicolon", -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - root.walkAtRules(atRule => { - if (atRule.parent === root) { return } - if (atRule !== atRule.parent.last) { return } - if (hasBlock(atRule)) { return } - checkLastNode(atRule) - }) - - root.walkDecls(decl => { - if (decl !== decl.parent.last) { return } - checkLastNode(decl) - }) - - function checkLastNode(node) { - let message - - if (expectation === "always") { - if (node.parent.raws.semicolon) { return } - message = messages.expected - } - if (expectation === "never") { - if (!node.parent.raws.semicolon) { return } - message = messages.rejected - } - - report({ - message, - node, - index: node.toString().trim().length - 1, - result, - ruleName, - }) - } - } -} diff --git a/src/rules/declaration-colon-space-after/index.js b/src/rules/declaration-colon-space-after/index.js deleted file mode 100644 index 3272e6a93d..0000000000 --- a/src/rules/declaration-colon-space-after/index.js +++ /dev/null @@ -1,70 +0,0 @@ -import { - declarationValueIndex, - isStandardSyntaxDeclaration, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -export const ruleName = "declaration-colon-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected single space after \":\"", - rejectedAfter: () => "Unexpected whitespace after \":\"", - expectedAfterSingleLine: () => "Expected single space after \":\" with a single-line declaration", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - ], - }) - if (!validOptions) { return } - - declarationColonSpaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - }) - } -} - -export function declarationColonSpaceChecker({ locationChecker, root, result, checkedRuleName }) { - root.walkDecls(decl => { - if (!isStandardSyntaxDeclaration(decl)) { return } - - // Get the raw prop, and only the prop - const endOfPropIndex = declarationValueIndex(decl) + decl.raws.between.length - 1 - - // The extra characters tacked onto the end ensure that there is a character to check - // after the colon. Otherwise, with `background:pink` the character after the - const propPlusColon = decl.toString().slice(0, endOfPropIndex) + "xxx" - - for (let i = 0, l = propPlusColon.length; i < l; i++) { - if (propPlusColon[i] !== ":") { continue } - locationChecker({ - source: propPlusColon, - index: i, - lineCheckStr: decl.value, - err: m => { - report({ - message: m, - node: decl, - index: decl.prop.toString().length + 1, - result, - ruleName: checkedRuleName, - }) - }, - }) - break - } - }) -} diff --git a/src/rules/declaration-colon-space-before/index.js b/src/rules/declaration-colon-space-before/index.js deleted file mode 100644 index f8c6fde40d..0000000000 --- a/src/rules/declaration-colon-space-before/index.js +++ /dev/null @@ -1,34 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { declarationColonSpaceChecker } from "../declaration-colon-space-after" - -export const ruleName = "declaration-colon-space-before" - -export const messages = ruleMessages(ruleName, { - expectedBefore: () => "Expected single space before \":\"", - rejectedBefore: () => "Unexpected whitespace before \":\"", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - declarationColonSpaceChecker({ - root, - result, - locationChecker: checker.before, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/declaration-empty-line-before/index.js b/src/rules/declaration-empty-line-before/index.js deleted file mode 100644 index e0b2455309..0000000000 --- a/src/rules/declaration-empty-line-before/index.js +++ /dev/null @@ -1,117 +0,0 @@ -import { - blockString, - hasEmptyLine, - isCustomProperty, - isSingleLineString, - isStandardSyntaxDeclaration, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "declaration-empty-line-before" - -export const messages = ruleMessages(ruleName, { - expected: "Expected empty line before declaration", - rejected: "Unexpected empty line before declaration", -}) - -export default function (expectation, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }, { - actual: options, - possible: { - except: [ - "first-nested", - "after-comment", - "after-declaration", - ], - ignore: [ - "after-comment", - "after-declaration", - "inside-single-line-block", - ], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop, parent } = decl - - if (!isStandardSyntaxDeclaration(decl)) { return } - if (isCustomProperty(prop)) { return } - - // Optionally ignore the node if a comment precedes it - if ( - optionsMatches(options, "ignore", "after-comment") - && decl.prev() - && decl.prev().type === "comment" - ) { - return - } - - // Optionally ignore the node if a declaration precedes it - if ( - optionsMatches(options, "ignore", "after-declaration") - && decl.prev() - && decl.prev().type === "decl" - ) { - return - } - - // Optionally ignore nodes inside single-line blocks - if ( - optionsMatches(options, "ignore", "inside-single-line-block") - && isSingleLineString(blockString(parent)) - ) { - return - } - - let expectEmptyLineBefore = (expectation === "always") ? true : false - - // Optionally reverse the expectation for the first nested node - if (optionsMatches(options, "except", "first-nested") - && decl === parent.first) { - expectEmptyLineBefore = !expectEmptyLineBefore - } - - // Optionally reverse the expectation if a comment precedes this node - if (optionsMatches(options, "except", "after-comment") - && decl.prev() - && decl.prev().type === "comment") { - expectEmptyLineBefore = !expectEmptyLineBefore - } - - // Optionally reverse the expectation if a declaration precedes this node - if (optionsMatches(options, "except", "after-declaration") - && decl.prev() - && decl.prev().prop - && isStandardSyntaxDeclaration(decl.prev()) - && !isCustomProperty(decl.prev().prop)) { - expectEmptyLineBefore = !expectEmptyLineBefore - } - - // Check for at least one empty line - const hasEmptyLineBefore = hasEmptyLine(decl.raws["before"]) - - // Return if the expectation is met - if (expectEmptyLineBefore === hasEmptyLineBefore) { return } - - const message = expectEmptyLineBefore ? messages.expected : messages.rejected - report({ - message, - node: decl, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/declaration-no-important/index.js b/src/rules/declaration-no-important/index.js deleted file mode 100644 index 77962bbf18..0000000000 --- a/src/rules/declaration-no-important/index.js +++ /dev/null @@ -1,30 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "declaration-no-important" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected !important", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkDecls(decl => { - if (!decl.important) { return } - - report({ - message: messages.rejected, - node: decl, - word: "important", - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/declaration-property-unit-blacklist/index.js b/src/rules/declaration-property-unit-blacklist/index.js deleted file mode 100644 index f9cb58a36e..0000000000 --- a/src/rules/declaration-property-unit-blacklist/index.js +++ /dev/null @@ -1,57 +0,0 @@ -import { - declarationValueIndex, - getUnitFromValueNode, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - find, - isObject, -} from "lodash" -import valueParser from "postcss-value-parser" -import { vendor } from "postcss" - -export const ruleName = "declaration-property-unit-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (property, unit) => `Unexpected unit "${unit}" for property "${property}"`, -}) - -export default function (blacklist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [isObject], - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop, value } = decl - const unprefixedProp = vendor.unprefixed(prop) - - const propBlacklist = find(blacklist, (list, propIdentifier) => matchesStringOrRegExp(unprefixedProp, propIdentifier)) - - if (!propBlacklist) { return } - - valueParser(value).walk(function (node) { - // Ignore wrong units within `url` function - if (node.type === "function" && node.value.toLowerCase() === "url") { return false } - if (node.type === "string") { return } - - const unit = getUnitFromValueNode(node) - - if (!unit || (unit && propBlacklist.indexOf(unit.toLowerCase()) === -1)) { return } - - report({ - message: messages.rejected(prop, unit), - node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/declaration-property-unit-whitelist/index.js b/src/rules/declaration-property-unit-whitelist/index.js deleted file mode 100644 index fffc37bc15..0000000000 --- a/src/rules/declaration-property-unit-whitelist/index.js +++ /dev/null @@ -1,57 +0,0 @@ -import { - declarationValueIndex, - getUnitFromValueNode, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - find, - isObject, -} from "lodash" -import valueParser from "postcss-value-parser" -import { vendor } from "postcss" - -export const ruleName = "declaration-property-unit-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (property, unit) => `Unexpected unit "${unit}" for property "${property}"`, -}) - -export default function (whitelist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [isObject], - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop, value } = decl - const unprefixedProp = vendor.unprefixed(prop) - - const propWhitelist = find(whitelist, (list, propIdentifier) => matchesStringOrRegExp(unprefixedProp, propIdentifier)) - - if (!propWhitelist) { return } - - valueParser(value).walk(function (node) { - // Ignore wrong units within `url` function - if (node.type === "function" && node.value.toLowerCase() === "url") { return false } - if (node.type === "string") { return } - - const unit = getUnitFromValueNode(node) - - if (!unit || (unit && propWhitelist.indexOf(unit.toLowerCase())) !== -1) { return } - - report({ - message: messages.rejected(prop, unit), - node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/declaration-property-value-blacklist/index.js b/src/rules/declaration-property-value-blacklist/index.js deleted file mode 100644 index 55bc8e1d21..0000000000 --- a/src/rules/declaration-property-value-blacklist/index.js +++ /dev/null @@ -1,45 +0,0 @@ -import { - find, - isEmpty, - isObject, -} from "lodash" -import { - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { vendor } from "postcss" - -export const ruleName = "declaration-property-value-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (property, value) => `Unexpected value "${value}" for property "${property}"`, -}) - -export default function (blacklist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [isObject], - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop, value } = decl - const unprefixedProp = vendor.unprefixed(prop) - const propBlacklist = find(blacklist, (list, propIdentifier) => matchesStringOrRegExp(unprefixedProp, propIdentifier)) - - if (isEmpty(propBlacklist)) { return } - - if (!matchesStringOrRegExp(value, propBlacklist)) { return } - - report({ - message: messages.rejected(prop, value), - node: decl, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/declaration-property-value-whitelist/index.js b/src/rules/declaration-property-value-whitelist/index.js deleted file mode 100644 index 24f75083d5..0000000000 --- a/src/rules/declaration-property-value-whitelist/index.js +++ /dev/null @@ -1,45 +0,0 @@ -import { - find, - isEmpty, - isObject, -} from "lodash" -import { - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { vendor } from "postcss" - -export const ruleName = "declaration-property-value-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (property, value) => `Unexpected value "${value}" for property "${property}"`, -}) - -export default function (whitelist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [isObject], - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop, value } = decl - const unprefixedProp = vendor.unprefixed(prop) - const propWhitelist = find(whitelist, (list, propIdentifier) => matchesStringOrRegExp(unprefixedProp, propIdentifier)) - - if (isEmpty(propWhitelist)) { return } - - if (matchesStringOrRegExp(value, propWhitelist)) { return } - - report({ - message: messages.rejected(prop, value), - node: decl, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/function-blacklist/index.js b/src/rules/function-blacklist/index.js deleted file mode 100644 index d58ce25590..0000000000 --- a/src/rules/function-blacklist/index.js +++ /dev/null @@ -1,47 +0,0 @@ -import { - declarationValueIndex, - isStandardSyntaxFunction, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import valueParser from "postcss-value-parser" -import { vendor } from "postcss" - -export const ruleName = "function-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (name) => `Unexpected function "${name}"`, -}) - -function rule(blacklist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [isString], - }) - if (!validOptions) { return } - root.walkDecls(decl => { - const { value } = decl - valueParser(value).walk(function (node) { - if (node.type !== "function") { return } - if (!isStandardSyntaxFunction(node)) { return } - if (!matchesStringOrRegExp(vendor.unprefixed(node.value).toLowerCase(), blacklist)) { return } - - report({ - message: messages.rejected(node.value), - node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, - result, - ruleName, - }) - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/function-comma-newline-after/index.js b/src/rules/function-comma-newline-after/index.js deleted file mode 100644 index 0f274bf32c..0000000000 --- a/src/rules/function-comma-newline-after/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { functionCommaSpaceChecker } from "../function-comma-space-after" - -export const ruleName = "function-comma-newline-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected newline after \",\"", - expectedAfterMultiLine: () => "Expected newline after \",\" in a multi-line function", - rejectedAfterMultiLine: () => "Unexpected whitespace after \",\" in a multi-line function", -}) - -export default function (expectation) { - const checker = whitespaceChecker("newline", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], - }) - if (!validOptions) { return } - - functionCommaSpaceChecker({ - root, - result, - locationChecker: checker.afterOneOnly, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/function-comma-space-after/index.js b/src/rules/function-comma-space-after/index.js deleted file mode 100644 index 7c485c1b77..0000000000 --- a/src/rules/function-comma-space-after/index.js +++ /dev/null @@ -1,94 +0,0 @@ -import { - declarationValueIndex, - isStandardSyntaxFunction, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import _ from "lodash" -import styleSearch from "style-search" -import valueParser from "postcss-value-parser" - -export const ruleName = "function-comma-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected single space after \",\"", - rejectedAfter: () => "Unexpected whitespace after \",\"", - expectedAfterSingleLine: () => "Expected single space after \",\" in a single-line function", - rejectedAfterSingleLine: () => "Unexpected whitespace after \",\" in a single-line function", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], - }) - if (!validOptions) { return } - - functionCommaSpaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - }) - } -} - -export function functionCommaSpaceChecker({ locationChecker, root, result, checkedRuleName }) { - root.walkDecls(decl => { - const declValue = _.get(decl, "raws.value.raw", decl.value) - - valueParser(declValue).walk(valueNode => { - if (valueNode.type !== "function") { return } - - if (!isStandardSyntaxFunction(valueNode)) { return } - - // Ignore `url()` arguments, which may contain data URIs or other funky stuff - if (valueNode.value.toLowerCase() === "url") { return } - - const functionArguments = (() => { - let result = valueParser.stringify(valueNode) - // Remove function name and opening paren - result = result.slice(valueNode.value.length + 1) - // Remove closing paren - result = result.slice(0, result.length - 1) - // 1. Remove comments including preceeding whitespace (when only succeeded by whitespace) - // 2. Remove all other comments, but leave adjacent whitespace intact - result = result.replace(/(\ *\/(\*.*\*\/(?!\S)|\/.*)|(\/(\*.*\*\/|\/.*)))/, "") - return result - })() - - styleSearch({ - source: functionArguments, - target: ",", - functionArguments: "skip", - }, (match) => { - locationChecker({ - source: functionArguments, - index: match.startIndex, - err: (message) => { - const index = declarationValueIndex(decl) + - valueNode.value.length + 1 + - valueNode.sourceIndex + - match.startIndex - report({ - index, - message, - node: decl, - result, - ruleName: checkedRuleName, - }) - }, - }) - }) - }) - }) -} diff --git a/src/rules/function-max-empty-lines/index.js b/src/rules/function-max-empty-lines/index.js deleted file mode 100644 index 60aedb396c..0000000000 --- a/src/rules/function-max-empty-lines/index.js +++ /dev/null @@ -1,59 +0,0 @@ -import { - isNumber, - repeat, -} from "lodash" -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "function-max-empty-lines" - -export const messages = ruleMessages(ruleName, { - expected: max => `Expected no more than ${max} empty line(s)`, -}) - -export default function (max) { - const maxAdjacentNewlines = max + 1 - - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: max, - possible: isNumber, - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - if (decl.value.indexOf("(") === -1) { return } - - const declString = decl.toString() - const repeatLFNewLines = repeat("\n", maxAdjacentNewlines) - const repeatCRLFNewLines = repeat("\r\n", maxAdjacentNewlines) - - styleSearch({ - source: declString, - target: "\n", - functionArguments: "only", - }, match => { - if ( - declString.substr(match.startIndex + 1, maxAdjacentNewlines) === repeatLFNewLines - || declString.substr(match.startIndex + 1, maxAdjacentNewlines * 2) === repeatCRLFNewLines - ) { - // Put index at `\r` if it's CRLF, otherwise leave it at `\n` - let index = match.startIndex - if (declString[index - 1] === "\r") { index -= 1 } - - report({ - message: messages.expected(max), - node: decl, - index, - result, - ruleName, - }) - } - }) - }) - } -} diff --git a/src/rules/function-url-data-uris/index.js b/src/rules/function-url-data-uris/index.js deleted file mode 100644 index 4e7a5b1e00..0000000000 --- a/src/rules/function-url-data-uris/index.js +++ /dev/null @@ -1,60 +0,0 @@ -import { - isStandardSyntaxValue, - isVariable, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import valueParser from "postcss-value-parser" - -export const ruleName = "function-url-data-uris" - -export const messages = ruleMessages(ruleName, { - expected: "Expected a data URI", - rejected: "Unexpected data URI", -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - root.walkDecls(function (decl) { - valueParser(decl.value).walk(valueNode => { - if (valueNode.type !== "function" - || valueNode.value.toLowerCase() !== "url" - || !valueNode.nodes.length > 0 - ) { return } - - const urlValueNode = valueNode.nodes[0] - - if (!urlValueNode.value - || !isStandardSyntaxValue(urlValueNode.value) - || isVariable(urlValueNode.value) - ) { return } - - const valueContainDataUris = urlValueNode.value.toLowerCase().indexOf("data:") === 0 - const needUrlDataUris = expectation === "always" - - if (valueContainDataUris && needUrlDataUris - || !valueContainDataUris && !needUrlDataUris - ) { return } - - const message = needUrlDataUris ? messages.expected : messages.rejected - - report({ - message, - node: decl, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/function-url-no-scheme-relative/index.js b/src/rules/function-url-no-scheme-relative/index.js deleted file mode 100644 index 2568befdc1..0000000000 --- a/src/rules/function-url-no-scheme-relative/index.js +++ /dev/null @@ -1,37 +0,0 @@ -import { - functionArgumentsSearch, - isStandardSyntaxUrl, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { trim } from "lodash" - -export const ruleName = "function-url-no-scheme-relative" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected scheme-relative url", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkDecls(function (decl) { - functionArgumentsSearch(decl.toString().toLowerCase(), "url", (args, index) => { - const url = trim(args, " '\"") - - if (!isStandardSyntaxUrl(url) || url.indexOf("//") !== 0) { return } - - report({ - message: messages.rejected, - node: decl, - index, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/function-url-scheme-whitelist/index.js b/src/rules/function-url-scheme-whitelist/index.js deleted file mode 100644 index d622d244fb..0000000000 --- a/src/rules/function-url-scheme-whitelist/index.js +++ /dev/null @@ -1,64 +0,0 @@ -import { - containsString, - functionArgumentsSearch, - isStandardSyntaxUrl, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString, trim } from "lodash" -import { parse } from "url" - -export const ruleName = "function-url-scheme-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (scheme) => `Unexpected url scheme "${scheme}:"`, -}) - -export default function (whitelist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkDecls(function (decl) { - functionArgumentsSearch(decl.toString().toLowerCase(), "url", (args, index) => { - const unspacedUrlString = trim(args, " ") - if (!isStandardSyntaxUrl(unspacedUrlString)) { return } - const urlString = trim(unspacedUrlString, "'\"") - - const url = parse(urlString) - if (url.protocol === null) { return } - - const scheme = url.protocol.toLowerCase().slice(0, -1) // strip trailing `:` - - // The URL spec does not require a scheme to be followed by `//`, but checking - // for it allows this rule to differentiate : urls from - // : urls. `data:` scheme urls are an exception to this rule. - const slashIndex = url.protocol.length - const expectedSlashes = urlString.slice(slashIndex, slashIndex + 2) - const isSchemeLessUrl = ( - expectedSlashes !== "//" && - scheme !== "data" - ) - if (isSchemeLessUrl) { return } - - const whitelistLowerCase = typeof whitelist === "string" - ? whitelist.toLowerCase() - : whitelist.join("|").toLowerCase().split("|") - - if (containsString(scheme, whitelistLowerCase)) { return } - - report({ - message: messages.rejected(scheme), - node: decl, - index, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/function-whitelist/index.js b/src/rules/function-whitelist/index.js deleted file mode 100644 index 8b9005fc39..0000000000 --- a/src/rules/function-whitelist/index.js +++ /dev/null @@ -1,47 +0,0 @@ -import { - declarationValueIndex, - isStandardSyntaxFunction, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import valueParser from "postcss-value-parser" -import { vendor } from "postcss" - -export const ruleName = "function-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (name) => `Unexpected function "${name}"`, -}) - -function rule(whitelistInput) { - const whitelist = [].concat(whitelistInput) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [isString], - }) - if (!validOptions) { return } - root.walkDecls(decl => { - const { value } = decl - valueParser(value).walk(function (node) { - if (node.type !== "function") { return } - if (!isStandardSyntaxFunction(node)) { return } - if (matchesStringOrRegExp(vendor.unprefixed(node.value).toLowerCase(), whitelist)) { return } - report({ - message: messages.rejected(node.value), - node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, - result, - ruleName, - }) - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/indentation/__tests__/at-rules.js b/src/rules/indentation/__tests__/at-rules.js deleted file mode 100644 index 2a38508999..0000000000 --- a/src/rules/indentation/__tests__/at-rules.js +++ /dev/null @@ -1,302 +0,0 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" - -const rule = rules[ruleName] - -testRule(rule, { - ruleName, - config: [2], - - accept: [ { - code: "@media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - "}", - }, { - code: "@media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - "}\n" + - "\n" + - "@media screen {\n" + - " b { color: orange; }\n" + - "}", - }, { - code: "@media print {\r\n" + - " a {\r\n" + - " color: pink;\r\n" + - " }\r\n" + - "}", - }, { - code: "@media print {\r\n" + - " a {\r\n" + - " color: pink;\r\n" + - " }\r\n" + - "}\r\n" + - "\r\n" + - "@media screen {\r\n" + - " b { color: orange; }\r\n" + - "}", - } ], - - reject: [ { - code: "\n" + - " @media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - "}", - - message: messages.expected("0 spaces"), - line: 2, - column: 3, - }, { - code: "@media print {\n" + - "a {\n" + - " color: pink;\n" + - " }\n" + - "}", - - message: messages.expected("2 spaces"), - line: 2, - column: 1, - }, { - code: "@media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - "}", - - message: messages.expected("4 spaces"), - line: 3, - column: 3, - }, { - code: "@media print {\n" + - " a {\n" + - " color: pink;\n" + - "}\n" + - "}", - - message: messages.expected("2 spaces"), - line: 4, - column: 1, - }, { - code: "@media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - "\t}", - - message: messages.expected("0 spaces"), - line: 5, - column: 2, - }, { - code: "\r\n" + - " @media print {\r\n" + - " a {\r\n" + - " color: pink;\r\n" + - " }\r\n" + - "}", - - message: messages.expected("0 spaces"), - line: 2, - column: 3, - }, { - code: "@media print {\r\n" + - "a {\r\n" + - " color: pink;\r\n" + - " }\r\n" + - "}", - - message: messages.expected("2 spaces"), - line: 2, - column: 1, - }, { - code: "@media print {\r\n" + - " a {\r\n" + - " color: pink;\r\n" + - " }\r\n" + - "}", - - message: messages.expected("4 spaces"), - line: 3, - column: 3, - }, { - code: "@media print {\r\n" + - " a {\r\n" + - " color: pink;\r\n" + - "}\r\n" + - "}", - - message: messages.expected("2 spaces"), - line: 4, - column: 1, - }, { - code: "@media print {\r\n" + - " a {\r\n" + - " color: pink;\r\n" + - " }\r\n" + - "\t}", - - message: messages.expected("0 spaces"), - line: 5, - column: 2, - } ], -}) - -testRule(rule, { - ruleName, - config: [ "tab", { except: ["block"] } ], - - accept: [ { - code: "@media print {\n" + - "\n" + - "a {\n" + - "\tcolor: pink;\n" + - "}\n" + - "\n" + - "}", - }, { - code: "@media print,\n" + - "\t(-webkit-min-device-pixel-ratio: 1.25),\n" + - "\t(min-resolution: 120dpi) {}", - } ], - - reject: [ { - code: "@media print {\n" + - "\n" + - "\ta {\n" + - "\tcolor: pink;\n" + - "}\n" + - "\n" + - "}", - - message: messages.expected("0 tabs"), - line: 3, - column: 2, - }, { - code: "@media print {\n" + - "\n" + - "a {\n" + - "color: pink;\n" + - "}\n" + - "\n" + - "}", - - message: messages.expected("1 tab"), - line: 4, - column: 1, - }, { - code: "@media print,\n" + - " (-webkit-min-device-pixel-ratio: 1.25),\n" + - "\t(min-resolution: 120dpi) {}", - - description: "multi-line at-rule params", - message: messages.expected("1 tab"), - line: 2, - column: 3, - } ], -}) - -testRule(rule, { - ruleName, - config: [ 4, { except: ["param"] } ], - - accept: [{ - code: "@media print,\n" + - "(-webkit-min-device-pixel-ratio: 1.25),\n" + - "(min-resolution: 120dpi) {}", - }], - - reject: [{ - code: "@media print,\n" + - " (-webkit-min-device-pixel-ratio: 1.25),\n" + - "(min-resolution: 120dpi) {}", - - message: messages.expected("0 spaces"), - line: 2, - column: 3, - }], -}) - -testRule(rule, { - ruleName, - config: [ 2, { ignore: ["param"] } ], - - accept: [ { - code: "@media print,\n" + - "(-webkit-min-device-pixel-ratio: 1.25),\n" + - "(min-resolution: 120dpi) {}", - }, { - code: "@media print,\n" + - " (-webkit-min-device-pixel-ratio: 1.25),\n" + - "(min-resolution: 120dpi) {}", - } ], - - reject: [{ - code: "\n" + - " @media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - "}", - - message: messages.expected("0 spaces"), - line: 2, - column: 3, - }], -}) - -testRule(rule, { - ruleName, - config: [ 2, { - indentClosingBrace: true, - } ], - - accept: [ { - code: "@media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - " }", - }, { - code: "@media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - " }\n" + - "\n" + - "@media screen {\n" + - " b { color: orange; }\n" + - " }", - } ], - - reject: [ { - code: "\n" + - "@media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - " }", - - message: messages.expected("2 spaces"), - line: 6, - column: 2, - }, { - code: "@media print {\n" + - " a {\n" + - " color: pink;\n" + - " }\n" + - " }", - - message: messages.expected("4 spaces"), - line: 4, - column: 4, - } ], -}) diff --git a/src/rules/indentation/__tests__/comments.js b/src/rules/indentation/__tests__/comments.js deleted file mode 100644 index e099580cc7..0000000000 --- a/src/rules/indentation/__tests__/comments.js +++ /dev/null @@ -1,95 +0,0 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" - -const rule = rules[ruleName] - -testRule(rule, { - ruleName, - config: ["tab"], - - accept: [ { - code: "/* blergh */", - }, { - code: ".foo {\n" + - "\t/* blergh */\n" + - "\ttop: 0;\n" + - "}", - }, { - code: "@media print {\n" + - "\t.foo {\n" + - "\t\t/* blergh */\n" + - "\t\ttop: 0;\n" + - "\t}\n" + - "}", - }, { - code: "/* blergh */", - }, { - code: ".foo {\r\n" + - "\t/* blergh */\r\n" + - "\ttop: 0;\r\n" + - "}", - }, { - code: "@media print {\r\n" + - "\t.foo {\r\n" + - "\t\t/* blergh */\r\n" + - "\t\ttop: 0;\r\n" + - "\t}\r\n" + - "}", - } ], - - reject: [ { - code: " /* blergh */", - message: messages.expected("0 tabs"), - line: 1, - column: 2, - }, { - code: ".foo {\n" + - "\t\t/* blergh */\n" + - "\ttop: 0;\n" + - "}", - - message: messages.expected("1 tab"), - line: 2, - column: 3, - }, { - code: "@media print {\n" + - "\t.foo {\n" + - "\t/* blergh */\n" + - "\t\ttop: 0;\n" + - "\t}\n" + - "}", - - message: messages.expected("2 tabs"), - line: 3, - column: 2, - }, { - code: " /* blergh */", - message: messages.expected("0 tabs"), - line: 1, - column: 2, - }, { - code: ".foo {\r\n" + - "\t\t/* blergh */\r\n" + - "\ttop: 0;\r\n" + - "}", - - message: messages.expected("1 tab"), - line: 2, - column: 3, - }, { - code: "@media print {\r\n" + - "\t.foo {\r\n" + - "\t/* blergh */\r\n" + - "\t\ttop: 0;\r\n" + - "\t}\r\n" + - "}", - - message: messages.expected("2 tabs"), - line: 3, - column: 2, - } ], -}) diff --git a/src/rules/indentation/__tests__/functions.js b/src/rules/indentation/__tests__/functions.js deleted file mode 100644 index e902e65f07..0000000000 --- a/src/rules/indentation/__tests__/functions.js +++ /dev/null @@ -1,629 +0,0 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" - -const rule = rules[ruleName] - -testRule(rule, { - ruleName, - config: [2], - skipBasicChecks: true, - - accept: [ { - code: ".foo {\n" + - " color: rgb(0, 0, 0);\n" + - "}", - }, { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - "}", - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - ");", - description: "sass-list", - }, { - code: "$colors: (\n" + - " primary: (\n" + - " base: $route;\n" + - " contrast: $white\n" + - " )\n" + - ");", - description: "nested Sass map", - }, { - code: "background:\n" + - " linear-gradient(\n" + - " to bottom,\n" + - " transparentize($gray-dark, 1) 0%,\n" + - " transparentize($gray-dark, 0.1) 100%\n" + - " );", - description: "nested parenthetical inside multiline value", - }, { - code: ".foo {\r\n" + - " color: rgb(0, 0, 0);\r\n" + - "}", - }, { - code: ".foo {\r\n" + - " color: rgb(\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );\r\n" + - " top: 0;\r\n" + - "}", - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - ");", - description: "sass-list", - }, { - code: "$colors: (\r\n" + - " primary: (\r\n" + - " base: $route;\r\n" + - " contrast: $white\r\n" + - " )\r\n" + - ");", - description: "nested Sass map", - }, { - code: "background:\r\n" + - " linear-gradient(\r\n" + - " to bottom,\r\n" + - " transparentize($gray-dark, 1) 0%,\r\n" + - " transparentize($gray-dark, 0.1) 100%\r\n" + - " );", - description: "nested parenthetical inside multiline value", - } ], - - reject: [ { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - "0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - "}", - message: messages.expected("4 spaces"), - line: 4, - column: 1, - }, { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - "}", - message: messages.expected("2 spaces"), - line: 6, - column: 5, - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - ");", - description: "sass-list", - message: messages.expected("2 spaces"), - line: 4, - column: 2, - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );", - description: "sass-list", - message: messages.expected("0 spaces"), - line: 5, - column: 3, - }, { - code: ".foo {\r\n" + - " color: rgb(\r\n" + - " 0,\r\n" + - "0,\r\n" + - " 0\r\n" + - " );\r\n" + - " top: 0;\r\n" + - "}", - message: messages.expected("4 spaces"), - line: 4, - column: 1, - }, { - code: ".foo {\r\n" + - " color: rgb(\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );\r\n" + - " top: 0;\r\n" + - "}", - message: messages.expected("2 spaces"), - line: 6, - column: 5, - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - ");", - description: "sass-list", - message: messages.expected("2 spaces"), - line: 4, - column: 2, - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );", - description: "sass-list", - message: messages.expected("0 spaces"), - line: 5, - column: 3, - } ], -}) - -testRule(rule, { - ruleName, - config: [ 2, { ignore: ["inside-parens"] } ], - skipBasicChecks: true, - - accept: [ { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - "}", - }, { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - "}", - }, { - code: ".foo {\n" + - " color: rgb(\n" + - "0,\n" + - "0,\n" + - "0\n" + - " );\n" + - "}", - }, { - code: ".foo {\n" + - " color: bar(\n" + - " rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " )\n" + - " );\n" + - "}", - }, { - code: ".foo {\n" + - " color: bar(\n" + - " rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " )\n" + - " );\n" + - "}", - }, { - code: "$tooltip-default-settings: (\n" + - " tooltip-gutter: 8px 10px,\n" + - " tooltip-border: 1px solid,\n" + - ");", - description: "Sass maps ignored", - } ], -}) - -testRule(rule, { - ruleName, - config: [ "tab", { "indentClosingBrace": false } ], - skipBasicChecks: true, - - accept: [{ - code: "$some-list: (\n" + - "\tvar: value,\n" + - "\tvar: value,\n" + - "\tvar: value\n" + - ");", - description: "tabbed sass-list with property value pairs", - }], - - reject: [{ - code: "$some-list: (\n" + - "\tvar: value,\n" + - "\tvar: value,\n" + - "\t\tvar: value\n" + - ");", - description: "tabbed sass-list with property value pairs", - message: messages.expected("1 tab"), - line: 4, - column: 3, - }], -}) - -testRule(rule, { - ruleName, - config: [ 2, { indentInsideParens: "twice" } ], - skipBasicChecks: true, - - accept: [ { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - "}", - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );", - description: "sass-list", - }, { - code: ".foo {\r\n" + - " color: rgb(\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );\r\n" + - " top: 0;\r\n" + - "}", - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );", - description: "sass-list", - } ], - - reject: [ { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - "}", - message: messages.expected("6 spaces"), - line: 4, - column: 5, - }, { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - "}", - message: messages.expected("4 spaces"), - line: 6, - column: 6, - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );", - description: "sass-list", - message: messages.expected("4 spaces"), - line: 4, - column: 4, - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );", - description: "sass-list", - message: messages.expected("2 spaces"), - line: 5, - column: 2, - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );", - description: "sass-list", - message: messages.expected("4 spaces"), - line: 4, - column: 4, - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );", - description: "sass-list", - message: messages.expected("2 spaces"), - line: 5, - column: 2, - } ], -}) - -testRule(rule, { - ruleName, - config: [ 2, { indentInsideParens: "once-at-root-twice-in-block" } ], - skipBasicChecks: true, - - accept: [ { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - "}", - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - ");", - description: "sass-list", - }, { - code: ".foo {\r\n" + - " color: rgb(\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );\r\n" + - " top: 0;\r\n" + - "}", - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - ");", - description: "sass-list", - } ], - - reject: [ { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - "}", - message: messages.expected("6 spaces"), - line: 4, - column: 5, - }, { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - "}", - message: messages.expected("4 spaces"), - line: 6, - column: 6, - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );", - description: "sass-list", - message: messages.expected("0 spaces"), - line: 5, - column: 3, - }, { - code: ".foo {\r\n" + - " color: rgb(\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );\r\n" + - " top: 0;\r\n" + - "}", - message: messages.expected("6 spaces"), - line: 4, - column: 5, - }, { - code: ".foo {\r\n" + - " color: rgb(\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );\r\n" + - " top: 0;\r\n" + - "}", - message: messages.expected("4 spaces"), - line: 6, - column: 6, - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );", - description: "sass-list", - message: messages.expected("0 spaces"), - line: 5, - column: 3, - } ], -}) - -testRule(rule, { - ruleName, - config: [ 2, { - indentInsideParens: "once-at-root-twice-in-block", - indentClosingBrace: true, - } ], - skipBasicChecks: true, - - accept: [ { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - " }", - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );", - description: "sass-list", - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );", - description: "sass-list", - } ], - - reject: [ { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - " }", - message: messages.expected("6 spaces"), - line: 4, - column: 5, - }, { - code: ".foo {\n" + - " color: rgb(\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );\n" + - " top: 0;\n" + - " }", - message: messages.expected("6 spaces"), - line: 6, - column: 6, - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - " );", - description: "sass-list", - message: messages.expected("2 spaces"), - line: 4, - column: 2, - }, { - code: "$some-list: (\n" + - " 0,\n" + - " 0,\n" + - " 0\n" + - ");", - description: "sass-list", - message: messages.expected("2 spaces"), - line: 5, - column: 1, - }, { - code: ".foo {\r\n" + - " color: rgb(\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );\r\n" + - " top: 0;\r\n" + - " }", - message: messages.expected("6 spaces"), - line: 4, - column: 5, - }, { - code: ".foo {\r\n" + - " color: rgb(\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );\r\n" + - " top: 0;\r\n" + - " }", - message: messages.expected("6 spaces"), - line: 6, - column: 6, - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - " );", - description: "sass-list", - message: messages.expected("2 spaces"), - line: 4, - column: 2, - }, { - code: "$some-list: (\r\n" + - " 0,\r\n" + - " 0,\r\n" + - " 0\r\n" + - ");", - description: "sass-list", - message: messages.expected("2 spaces"), - line: 5, - column: 1, - } ], -}) - -testRule(rule, { - ruleName, - config: [2], - syntax: "less", - skipBasicChecks: true, - - accept: [ { - code: ".foo {\n" + - " .mixin(\n" + - " @foo,\n" + - " @bar,\n" + - " @baz\n" + - " );\n" + - "}", - description: "Less mixin with multi-line arguments", - }, { - code: ".foo {\r\n" + - " .mixin(\r\n" + - " @foo,\r\n" + - " @bar,\r\n" + - " @baz\r\n" + - " );\r\n" + - "}", - description: "Less mixin with multi-line arguments", - } ], -}) diff --git a/src/rules/indentation/__tests__/rules.js b/src/rules/indentation/__tests__/rules.js deleted file mode 100644 index 38b2e79c4d..0000000000 --- a/src/rules/indentation/__tests__/rules.js +++ /dev/null @@ -1,615 +0,0 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" - -const rule = rules[ruleName] - -testRule(rule, { - ruleName, - config: [2], - - accept: [ { - code: "/* anything\n" + - " goes\n" + - "\t\t\twithin a comment */\n" + - "", - }, { - code: "a { top: 0; } b { top: 1px; }", - }, { - code: "a {\n" + - " top: 0;\n" + - "}\n" + - "b { top: 1px; bottom: 4px; }", - }, { - code: "a {\n" + - " top: 0;\n" + - "} b { top: 1px; }", - }, { - code: "a {\n" + - " color: pink;\n" + - "}", - }, { - code: "a { color: pink;\n" + - "}", - }, { - code: "a {\n" + - " color: pink;\n" + - "} b { top: 0; }", - }, { - code: "a { color: pink;\n" + - " top: 0; background: orange;\n" + - "}", - }, { - code: "a {\n" + - " color: pink;\n" + - "}\n" + - "\n" + - "\n" + - "b {\n" + - " color: orange\n" + - "}", - }, { - code: "a {\n" + - " color: pink;}", - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left;\n" + - " color: pink;\n" + - "}", - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left\n" + - " ;\n" + - "}", - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - "\n" + - " bottom left\n" + - " ;\n" + - "}", - - description: "weird empty line", - }, { - code: "a {\n" + - " *top: 1px;\n" + - "}", - }, { - code: "a {\n" + - " _top: 1px;\n" + - "}", - }, { - code: "* { top: 0; }", - }, { - code: "@media print {\n" + - " * { color: pink; }\n" + - "}", - }, { - code: "/* anything\r\n" + - " goes\r\n" + - "\t\t\twithin a comment */\r\n" + - "", - }, { - code: "a {\r\n" + - " top: 0;\r\n" + - "}\r\n" + - "b { top: 1px; bottom: 4px; }", - }, { - code: "a {\r\n" + - " top: 0;\r\n" + - "} b { top: 1px; }", - }, { - code: "a {\r\n" + - " color: pink;\r\n" + - "}", - }, { - code: "a { color: pink;\r\n" + - "}", - }, { - code: "a {\r\n" + - " color: pink;\r\n" + - "} b { top: 0; }", - }, { - code: "a { color: pink;\r\n" + - " top: 0; background: orange;\r\n" + - "}", - }, { - code: "a {\r\n" + - " color: pink;\r\n" + - "}\r\n" + - "\r\n" + - "\r\n" + - "b {\r\n" + - " color: orange\r\n" + - "}", - }, { - code: "a {\r\n" + - " color: pink;}", - }, { - code: "a {\r\n" + - " background-position: top left,\r\n" + - " top right,\r\n" + - " bottom left;\r\n" + - " color: pink;\r\n" + - "}", - }, { - code: "a {\r\n" + - " background-position: top left,\r\n" + - " top right,\r\n" + - " bottom left\r\n" + - " ;\r\n" + - "}", - }, { - code: "a {\r\n" + - " background-position: top left,\r\n" + - " top right,\r\n" + - "\r\n" + - " bottom left\r\n" + - " ;\r\n" + - "}", - - description: "weird empty line", - }, { - code: "a {\r\n" + - " *top: 1px;\r\n" + - "}", - }, { - code: "a {\r\n" + - " _top: 1px;\r\n" + - "}", - }, { - code: "* { top: 0; }", - }, { - code: "@media print {\r\n" + - " * { color: pink; }\r\n" + - "}", - } ], - - reject: [ { - code: "\ta {\n" + - " color: pink;\n" + - "}", - - message: messages.expected("0 spaces"), - line: 1, - column: 2, - }, { - code: "a {\n" + - " color: pink;\n" + - " }", - - message: messages.expected("0 spaces"), - line: 3, - column: 3, - }, { - code: "a,\n" + - "b {\n" + - " color: pink;\n" + - " }", - - message: messages.expected("0 spaces"), - line: 4, - column: 3, - }, { - code: "a { color: pink;\n" + - " }", - - message: messages.expected("0 spaces"), - line: 2, - column: 3, - }, { - code: "a {\n" + - " color: pink\n" + - "}\n" + - " b {\n" + - " color: orange\n" + - "}", - - message: messages.expected("0 spaces"), - line: 4, - column: 2, - }, { - code: "a {\n" + - " color: pink\n" + - "}\n" + - "b {\n" + - " color: orange\n" + - " }", - - message: messages.expected("0 spaces"), - line: 6, - column: 2, - }, { - code: "a {\n" + - "color: pink;\n" + - "}", - - message: messages.expected("2 spaces"), - line: 2, - column: 1, - }, { - code: "a {\n" + - "\tcolor: pink;\n" + - "}", - - message: messages.expected("2 spaces"), - line: 2, - column: 2, - }, { - code: "a {\n" + - " color: pink;\n" + - " background: orange;\n" + - "}", - - message: messages.expected("2 spaces"), - line: 3, - column: 2, - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left;\n" + - " color: pink;\n" + - "}", - - message: messages.expected("4 spaces"), - line: 3, - column: 3, - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left;\n" + - " color: pink;\n" + - "}", - - message: messages.expected("4 spaces"), - line: 4, - column: 3, - }, { - code: "@media print {\n" + - " * { color: pink; }\n" + - "}", - - message: messages.expected("2 spaces"), - }, { - code: "\ta {\r\n" + - " color: pink;\r\n" + - "}", - - message: messages.expected("0 spaces"), - line: 1, - column: 2, - }, { - code: "a {\r\n" + - " color: pink;\r\n" + - " }", - - message: messages.expected("0 spaces"), - line: 3, - column: 3, - }, { - code: "a,\r\n" + - "b {\r\n" + - " color: pink;\r\n" + - " }", - - message: messages.expected("0 spaces"), - line: 4, - column: 3, - }, { - code: "a { color: pink;\r\n" + - " }", - - message: messages.expected("0 spaces"), - line: 2, - column: 3, - }, { - code: "a {\r\n" + - " color: pink\r\n" + - "}\r\n" + - " b {\r\n" + - " color: orange\r\n" + - "}", - - message: messages.expected("0 spaces"), - line: 4, - column: 2, - }, { - code: "a {\r\n" + - " color: pink\r\n" + - "}\r\n" + - "b {\r\n" + - " color: orange\r\n" + - " }", - - message: messages.expected("0 spaces"), - line: 6, - column: 2, - }, { - code: "a {\r\n" + - "color: pink;\r\n" + - "}", - - message: messages.expected("2 spaces"), - line: 2, - column: 1, - }, { - code: "a {\r\n" + - "\tcolor: pink;\r\n" + - "}", - - message: messages.expected("2 spaces"), - line: 2, - column: 2, - }, { - code: "a {\r\n" + - " color: pink;\r\n" + - " background: orange;\r\n" + - "}", - - message: messages.expected("2 spaces"), - line: 3, - column: 2, - }, { - code: "a {\r\n" + - " background-position: top left,\r\n" + - " top right,\r\n" + - " bottom left;\r\n" + - " color: pink;\r\n" + - "}", - - message: messages.expected("4 spaces"), - line: 3, - column: 3, - }, { - code: "a {\r\n" + - " background-position: top left,\r\n" + - " top right,\r\n" + - " bottom left;\r\n" + - " color: pink;\r\n" + - "}", - - message: messages.expected("4 spaces"), - line: 4, - column: 3, - }, { - code: "@media print {\r\n" + - " * { color: pink; }\r\n" + - "}", - - message: messages.expected("2 spaces"), - } ], -}) - -testRule(rule, { - ruleName, - config: ["tab"], - - accept: [ { - code: "", - }, { - code: "a {color: pink;}", - }, { - code: "a {\n" + - "\tcolor: pink;\n" + - "}", - }, { - code: "a {\n" + - "\tcolor: pink;\n" + - "}\n" + - "\n" + - "b {\n" + - "\tcolor: orange\n" + - "}", - }, { - code: "a {\n" + - "\tcolor: pink;}", - } ], - - reject: [ { - code: "\ta {\n" + - "\tcolor: pink;\n" + - "}", - - message: messages.expected("0 tabs"), - line: 1, - column: 2, - }, { - code: "a {\n" + - "\tcolor: pink;\n" + - " }", - - message: messages.expected("0 tabs"), - line: 3, - column: 3, - }, { - code: "a {\n" + - "\tcolor: pink\n" + - "}\n" + - " b {\n" + - "\tcolor: orange\n" + - "}", - - message: messages.expected("0 tabs"), - line: 4, - column: 2, - }, { - code: "a {\n" + - "\tcolor: pink\n" + - "}\n" + - "b {\n" + - "\tcolor: orange\n" + - " }", - - message: messages.expected("0 tabs"), - line: 6, - column: 2, - }, { - code: "a {\n" + - "color: pink;\n" + - "}", - - message: messages.expected("1 tab"), - line: 2, - column: 1, - }, { - code: "a {\n" + - " color: pink;\n" + - "}", - - message: messages.expected("1 tab"), - line: 2, - column: 3, - }, { - code: "a {\n" + - "\tcolor: pink;\n" + - " background: orange;\n" + - "}", - - message: messages.expected("1 tab"), - line: 3, - column: 2, - }, { - code: "a { color: pink;\n" + - "top: 0; background: orange;\n" + - "}", - - message: messages.expected("1 tab"), - line: 2, - column: 1, - } ], -}) - -testRule(rule, { - ruleName, - config: [ 2, { except: ["value"] } ], - - accept: [ { - code: "a {\n" + - " background-position: top left, top right, bottom left;\n" + - " color: pink;\n" + - "}", - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left;\n" + - " color: pink;\n" + - "}", - } ], - - reject: [ { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left;\n" + - " color: pink;\n" + - "}", - - message: messages.expected("2 spaces"), - line: 3, - column: 5, - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left;\n" + - " color: pink;\n" + - "}", - - message: messages.expected("2 spaces"), - line: 4, - column: 5, - } ], -}) - -testRule(rule, { - ruleName, - config: [ 2, { ignore: ["value"] } ], - - accept: [ { - code: "a {\n" + - " background-position: top left, top right, bottom left;\n" + - " color: pink;\n" + - "}", - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left;\n" + - " color: pink;\n" + - "}", - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left;\n" + - " color: pink;\n" + - "}", - }, { - code: "a {\n" + - " background-position: top left,\n" + - " top right,\n" + - " bottom left;\n" + - " color: pink;\n" + - "}", - } ], - - reject: [{ - code: "\ta {\n" + - " color: pink;\n" + - "}", - - message: messages.expected("0 spaces"), - line: 1, - column: 2, - }], -}) - -testRule(rule, { - ruleName, - config: [ 2, { - indentClosingBrace: true, - } ], - - accept: [ { - code: "a {\n" + - " color: pink;\n" + - " }", - }, { - code: "a {\n" + - " color: pink;\n" + - " & b {\n" + - " top: 0;\n" + - " }\n" + - " }", - } ], - - reject: [ { - code: "a {\n" + - " color: pink;\n" + - "}", - message: messages.expected("2 spaces"), - line: 3, - column: 1, - }, { - code: "a {\n" + - " color: pink;\n" + - " & b {\n" + - " top: 0;\n" + - " }\n" + - " }", - message: messages.expected("4 spaces"), - line: 5, - column: 4, - } ], -}) diff --git a/src/rules/indentation/__tests__/selectors.js b/src/rules/indentation/__tests__/selectors.js deleted file mode 100644 index 410c61c79b..0000000000 --- a/src/rules/indentation/__tests__/selectors.js +++ /dev/null @@ -1,142 +0,0 @@ -import { - messages, - ruleName, -} from ".." -import rules from "../../../rules" -import { testRule } from "../../../testUtils" - -const rule = rules[ruleName] - -testRule(rule, { - ruleName, - config: [2], - skipBasicChecks: true, - - accept: [ { - code: "a { color: pink; }", - }, { - code: "a,\n" + - "b { color: pink; }", - }, { - code: "a,\n" + - "b,\n" + - "c { color: pink; }", - }, { - code: "@media print {\n" + - " a,\n" + - " b { color: pink;}\n" + - "}", - }, { - code: "a {\n" + - " @nest b & ,\n" + - " &.foo {\n" + - " color: pink;\n" + - " }\n" + - "}", - }, { - code: "a,\r\n" + - "b { color: pink; }", - }, { - code: "a,\r\n" + - "b,\r\n" + - "c { color: pink; }", - }, { - code: "@media print {\r\n" + - " a,\r\n" + - " b { color: pink;}\r\n" + - "}", - }, { - code: "a {\r\n" + - " @nest b & ,\r\n" + - " &.foo {\r\n" + - " color: pink;\r\n" + - " }\r\n" + - "}", - } ], - - reject: [ { - code: "a,\n" + - " b { color: pink; }", - - message: messages.expected("0 spaces"), - line: 2, - column: 3, - }, { - code: "a,\n" + - "b,\n" + - " c { color: pink; }", - - message: messages.expected("0 spaces"), - line: 3, - column: 2, - }, { - code: "@media print {\n" + - " a,\n" + - "b { color: pink;}\n" + - "}", - - message: messages.expected("2 spaces"), - line: 3, - column: 1, - }, { - code: "@media print {\n" + - " a,\n" + - " b { color: pink;}\n" + - "}", - - message: messages.expected("2 spaces"), - line: 3, - column: 4, - }, { - code: "@media print {\n" + - " a,\n" + - " b { color: pink;}\n" + - "}", - - message: messages.expected("2 spaces"), - line: 2, - column: 4, - }, { - code: "a,\r\n" + - " b { color: pink; }", - - message: messages.expected("0 spaces"), - line: 2, - column: 3, - }, { - code: "a,\r\n" + - "b,\r\n" + - " c { color: pink; }", - - message: messages.expected("0 spaces"), - line: 3, - column: 2, - }, { - code: "@media print {\r\n" + - " a,\r\n" + - "b { color: pink;}\r\n" + - "}", - - message: messages.expected("2 spaces"), - line: 3, - column: 1, - }, { - code: "@media print {\r\n" + - " a,\r\n" + - " b { color: pink;}\r\n" + - "}", - - message: messages.expected("2 spaces"), - line: 3, - column: 4, - }, { - code: "@media print {\r\n" + - " a,\r\n" + - " b { color: pink;}\r\n" + - "}", - - message: messages.expected("2 spaces"), - line: 2, - column: 4, - } ], -}) diff --git a/src/rules/index.js b/src/rules/index.js deleted file mode 100644 index 2f291c6b68..0000000000 --- a/src/rules/index.js +++ /dev/null @@ -1,347 +0,0 @@ -import atRuleBlacklist from "./at-rule-blacklist" -import atRuleEmptyLineBefore from "./at-rule-empty-line-before" -import atRuleNameCase from "./at-rule-name-case" -import atRuleNameNewlineAfter from "./at-rule-name-newline-after" -import atRuleNameSpaceAfter from "./at-rule-name-space-after" -import atRuleNoUnknown from "./at-rule-no-unknown" -import atRuleNoVendorPrefix from "./at-rule-no-vendor-prefix" -import atRuleSemicolonNewlineAfter from "./at-rule-semicolon-newline-after" -import atRuleWhitelist from "./at-rule-whitelist" -import blockClosingBraceEmptyLineBefore from "./block-closing-brace-empty-line-before" -import blockClosingBraceNewlineAfter from "./block-closing-brace-newline-after" -import blockClosingBraceNewlineBefore from "./block-closing-brace-newline-before" -import blockClosingBraceSpaceAfter from "./block-closing-brace-space-after" -import blockClosingBraceSpaceBefore from "./block-closing-brace-space-before" -import blockNoEmpty from "./block-no-empty" -import blockNoSingleLine from "./block-no-single-line" -import blockOpeningBraceNewlineAfter from "./block-opening-brace-newline-after" -import blockOpeningBraceNewlineBefore from "./block-opening-brace-newline-before" -import blockOpeningBraceSpaceAfter from "./block-opening-brace-space-after" -import blockOpeningBraceSpaceBefore from "./block-opening-brace-space-before" -import colorHexCase from "./color-hex-case" -import colorHexLength from "./color-hex-length" -import colorNamed from "./color-named" -import colorNoHex from "./color-no-hex" -import colorNoInvalidHex from "./color-no-invalid-hex" -import commentEmptyLineBefore from "./comment-empty-line-before" -import commentNoEmpty from "./comment-no-empty" -import commentWhitespaceInside from "./comment-whitespace-inside" -import commentWordBlacklist from "./comment-word-blacklist" -import customMediaPattern from "./custom-media-pattern" -import customPropertyEmptyLineBefore from "./custom-property-empty-line-before" -import customPropertyNoOutsideRoot from "./custom-property-no-outside-root" -import customPropertyPattern from "./custom-property-pattern" -import declarationBangSpaceAfter from "./declaration-bang-space-after" -import declarationBangSpaceBefore from "./declaration-bang-space-before" -import declarationBlockNoDuplicateProperties from "./declaration-block-no-duplicate-properties" -import declarationBlockNoIgnoredProperties from "./declaration-block-no-ignored-properties" -import declarationBlockNoRedundantLonghandProperties from "./declaration-block-no-redundant-longhand-properties" -import declarationBlockNoShorthandPropertyOverrides from "./declaration-block-no-shorthand-property-overrides" -import declarationBlockPropertiesOrder from "./declaration-block-properties-order" -import declarationBlockSemicolonNewlineAfter from "./declaration-block-semicolon-newline-after" -import declarationBlockSemicolonNewlineBefore from "./declaration-block-semicolon-newline-before" -import declarationBlockSemicolonSpaceAfter from "./declaration-block-semicolon-space-after" -import declarationBlockSemicolonSpaceBefore from "./declaration-block-semicolon-space-before" -import declarationBlockSingleLineMaxDeclarations from "./declaration-block-single-line-max-declarations" -import declarationBlockTrailingSemicolon from "./declaration-block-trailing-semicolon" -import declarationColonNewlineAfter from "./declaration-colon-newline-after" -import declarationColonSpaceAfter from "./declaration-colon-space-after" -import declarationColonSpaceBefore from "./declaration-colon-space-before" -import declarationEmptyLineBefore from "./declaration-empty-line-before" -import declarationNoImportant from "./declaration-no-important" -import declarationPropertyUnitBlacklist from "./declaration-property-unit-blacklist" -import declarationPropertyUnitWhitelist from "./declaration-property-unit-whitelist" -import declarationPropertyValueBlacklist from "./declaration-property-value-blacklist" -import declarationPropertyValueWhitelist from "./declaration-property-value-whitelist" -import fontFamilyNameQuotes from "./font-family-name-quotes" -import fontFamilyNoDuplicateNames from "./font-family-no-duplicate-names" -import fontWeightNotation from "./font-weight-notation" -import functionBlacklist from "./function-blacklist" -import functionCalcNoUnspacedOperator from "./function-calc-no-unspaced-operator" -import functionCommaNewlineAfter from "./function-comma-newline-after" -import functionCommaNewlineBefore from "./function-comma-newline-before" -import functionCommaSpaceAfter from "./function-comma-space-after" -import functionCommaSpaceBefore from "./function-comma-space-before" -import functionLinearGradientNoNonstandardDirection from "./function-linear-gradient-no-nonstandard-direction" -import functionMaxEmptyLines from "./function-max-empty-lines" -import functionNameCase from "./function-name-case" -import functionParenthesesNewlineInside from "./function-parentheses-newline-inside" -import functionParenthesesSpaceInside from "./function-parentheses-space-inside" -import functionUrlDataUris from "./function-url-data-uris" -import functionUrlNoSchemeRelative from "./function-url-no-scheme-relative" -import functionUrlQuotes from "./function-url-quotes" -import functionUrlSchemeWhitelist from "./function-url-scheme-whitelist" -import functionWhitelist from "./function-whitelist" -import functionWhitespaceAfter from "./function-whitespace-after" -import indentation from "./indentation" -import keyframeDeclarationNoImportant from "./keyframe-declaration-no-important" -import lengthZeroNoUnit from "./length-zero-no-unit" -import maxEmptyLines from "./max-empty-lines" -import maxLineLength from "./max-line-length" -import maxNestingDepth from "./max-nesting-depth" -import mediaFeatureColonSpaceAfter from "./media-feature-colon-space-after" -import mediaFeatureColonSpaceBefore from "./media-feature-colon-space-before" -import mediaFeatureNameBlacklist from "./media-feature-name-blacklist" -import mediaFeatureNameCase from "./media-feature-name-case" -import mediaFeatureNameNoUnknown from "./media-feature-name-no-unknown" -import mediaFeatureNameNoVendorPrefix from "./media-feature-name-no-vendor-prefix" -import mediaFeatureNameWhitelist from "./media-feature-name-whitelist" -import mediaFeatureNoMissingPunctuation from "./media-feature-no-missing-punctuation" -import mediaFeatureParenthesesSpaceInside from "./media-feature-parentheses-space-inside" -import mediaFeatureRangeOperatorSpaceAfter from "./media-feature-range-operator-space-after" -import mediaFeatureRangeOperatorSpaceBefore from "./media-feature-range-operator-space-before" -import mediaQueryListCommaNewlineAfter from "./media-query-list-comma-newline-after" -import mediaQueryListCommaNewlineBefore from "./media-query-list-comma-newline-before" -import mediaQueryListCommaSpaceAfter from "./media-query-list-comma-space-after" -import mediaQueryListCommaSpaceBefore from "./media-query-list-comma-space-before" -import noBrowserHacks from "./no-browser-hacks" -import noDescendingSpecificity from "./no-descending-specificity" -import noDuplicateSelectors from "./no-duplicate-selectors" -import noEmptySource from "./no-empty-source" -import noEolWhitespace from "./no-eol-whitespace" -import noExtraSemicolons from "./no-extra-semicolons" -import noIndistinguishableColors from "./no-indistinguishable-colors" -import noInvalidDoubleSlashComments from "./no-invalid-double-slash-comments" -import noMissingEndOfSourceNewline from "./no-missing-end-of-source-newline" -import noSupportedBrowserFeatures from "./no-unsupported-browser-features" -import noUnknownAnimations from "./no-unknown-animations" -import numberLeadingZero from "./number-leading-zero" -import numberMaxPrecision from "./number-max-precision" -import numberNoTrailingZeros from "./number-no-trailing-zeros" -import propertyBlacklist from "./property-blacklist" -import propertyCase from "./property-case" -import propertyNoUnknown from "./property-no-unknown" -import propertyNoVendorPrefix from "./property-no-vendor-prefix" -import propertyWhitelist from "./property-whitelist" -import rootNoStandardProperties from "./root-no-standard-properties" -import ruleNestedEmptyLineBefore from "./rule-nested-empty-line-before" -import ruleNonNestedEmptyLineBefore from "./rule-non-nested-empty-line-before" -import selectorAttributeBracketsSpaceInside from "./selector-attribute-brackets-space-inside" -import selectorAttributeOperatorBlacklist from "./selector-attribute-operator-blacklist" -import selectorAttributeOperatorSpaceAfter from "./selector-attribute-operator-space-after" -import selectorAttributeOperatorSpaceBefore from "./selector-attribute-operator-space-before" -import selectorAttributeOperatorWhitelist from "./selector-attribute-operator-whitelist" -import selectorAttributeQuotes from "./selector-attribute-quotes" -import selectorClassPattern from "./selector-class-pattern" -import selectorCombinatorSpaceAfter from "./selector-combinator-space-after" -import selectorCombinatorSpaceBefore from "./selector-combinator-space-before" -import selectorDescendantCombinatorNoNonSpace from "./selector-descendant-combinator-no-non-space" -import selectorIdPattern from "./selector-id-pattern" -import selectorListCommaNewlineAfter from "./selector-list-comma-newline-after" -import selectorListCommaNewlineBefore from "./selector-list-comma-newline-before" -import selectorListCommaSpaceAfter from "./selector-list-comma-space-after" -import selectorListCommaSpaceBefore from "./selector-list-comma-space-before" -import selectorMaxCompoundSelectors from "./selector-max-compound-selectors" -import selectorMaxEmptyLines from "./selector-max-empty-lines" -import selectorMaxSpecificity from "./selector-max-specificity" -import selectorNestedPattern from "./selector-nested-pattern" -import selectorNoAttribute from "./selector-no-attribute" -import selectorNoCombinator from "./selector-no-combinator" -import selectorNoEmpty from "./selector-no-empty" -import selectorNoId from "./selector-no-id" -import selectorNoQualifyingType from "./selector-no-qualifying-type" -import selectorNoType from "./selector-no-type" -import selectorNoUniversal from "./selector-no-universal" -import selectorNoVendorPrefix from "./selector-no-vendor-prefix" -import selectorPseudoClassBlacklist from "./selector-pseudo-class-blacklist" -import selectorPseudoClassCase from "./selector-pseudo-class-case" -import selectorPseudoClassNoUnknown from "./selector-pseudo-class-no-unknown" -import selectorPseudoClassParenthesesSpaceInside from "./selector-pseudo-class-parentheses-space-inside" -import selectorPseudoClassWhitelist from "./selector-pseudo-class-whitelist" -import selectorPseudoElementCase from "./selector-pseudo-element-case" -import selectorPseudoElementColonNotation from "./selector-pseudo-element-colon-notation" -import selectorPseudoElementNoUnknown from "./selector-pseudo-element-no-unknown" -import selectorRootNoComposition from "./selector-root-no-composition" -import selectorTypeCase from "./selector-type-case" -import selectorTypeNoUnknown from "./selector-type-no-unknown" -import shorthandPropertyNoRedundantValues from "./shorthand-property-no-redundant-values" -import stringNoNewline from "./string-no-newline" -import stringQuotes from "./string-quotes" -import stylelintDisableReason from "./stylelint-disable-reason" -import timeNoImperceptible from "./time-no-imperceptible" -import unitBlacklist from "./unit-blacklist" -import unitCase from "./unit-case" -import unitNoUnknown from "./unit-no-unknown" -import unitWhitelist from "./unit-whitelist" -import valueKeywordCase from "./value-keyword-case" -import valueListCommaNewlineAfter from "./value-list-comma-newline-after" -import valueListCommaNewlineBefore from "./value-list-comma-newline-before" -import valueListCommaSpaceAfter from "./value-list-comma-space-after" -import valueListCommaSpaceBefore from "./value-list-comma-space-before" -import valueListMaxEmptyLines from "./value-list-max-empty-lines" -import valueNoVendorPrefix from "./value-no-vendor-prefix" - -export default { - "at-rule-blacklist": atRuleBlacklist, - "at-rule-empty-line-before": atRuleEmptyLineBefore, - "at-rule-name-case": atRuleNameCase, - "at-rule-name-newline-after": atRuleNameNewlineAfter, - "at-rule-name-space-after": atRuleNameSpaceAfter, - "at-rule-no-unknown": atRuleNoUnknown, - "at-rule-no-vendor-prefix": atRuleNoVendorPrefix, - "at-rule-semicolon-newline-after": atRuleSemicolonNewlineAfter, - "at-rule-whitelist": atRuleWhitelist, - "block-closing-brace-empty-line-before": blockClosingBraceEmptyLineBefore, - "block-closing-brace-newline-after": blockClosingBraceNewlineAfter, - "block-closing-brace-newline-before": blockClosingBraceNewlineBefore, - "block-closing-brace-space-after": blockClosingBraceSpaceAfter, - "block-closing-brace-space-before": blockClosingBraceSpaceBefore, - "block-no-empty": blockNoEmpty, - "block-no-single-line": blockNoSingleLine, - "block-opening-brace-newline-after": blockOpeningBraceNewlineAfter, - "block-opening-brace-newline-before": blockOpeningBraceNewlineBefore, - "block-opening-brace-space-after": blockOpeningBraceSpaceAfter, - "block-opening-brace-space-before": blockOpeningBraceSpaceBefore, - "color-hex-case": colorHexCase, - "color-hex-length": colorHexLength, - "color-named": colorNamed, - "color-no-hex": colorNoHex, - "color-no-invalid-hex": colorNoInvalidHex, - "comment-empty-line-before": commentEmptyLineBefore, - "comment-no-empty": commentNoEmpty, - "comment-whitespace-inside": commentWhitespaceInside, - "comment-word-blacklist": commentWordBlacklist, - "custom-media-pattern": customMediaPattern, - "custom-property-empty-line-before": customPropertyEmptyLineBefore, - "custom-property-no-outside-root": customPropertyNoOutsideRoot, - "custom-property-pattern": customPropertyPattern, - "declaration-bang-space-after": declarationBangSpaceAfter, - "declaration-bang-space-before": declarationBangSpaceBefore, - "declaration-block-no-duplicate-properties": declarationBlockNoDuplicateProperties, - "declaration-block-no-ignored-properties": declarationBlockNoIgnoredProperties, - "declaration-block-no-redundant-longhand-properties": declarationBlockNoRedundantLonghandProperties, - "declaration-block-no-shorthand-property-overrides": declarationBlockNoShorthandPropertyOverrides, - "declaration-block-properties-order": declarationBlockPropertiesOrder, - "declaration-block-semicolon-newline-after": declarationBlockSemicolonNewlineAfter, - "declaration-block-semicolon-newline-before": declarationBlockSemicolonNewlineBefore, - "declaration-block-semicolon-space-after": declarationBlockSemicolonSpaceAfter, - "declaration-block-semicolon-space-before": declarationBlockSemicolonSpaceBefore, - "declaration-block-single-line-max-declarations": declarationBlockSingleLineMaxDeclarations, - "declaration-block-trailing-semicolon": declarationBlockTrailingSemicolon, - "declaration-colon-newline-after": declarationColonNewlineAfter, - "declaration-colon-space-after": declarationColonSpaceAfter, - "declaration-colon-space-before": declarationColonSpaceBefore, - "declaration-empty-line-before": declarationEmptyLineBefore, - "declaration-no-important": declarationNoImportant, - "declaration-property-unit-blacklist": declarationPropertyUnitBlacklist, - "declaration-property-unit-whitelist": declarationPropertyUnitWhitelist, - "declaration-property-value-blacklist": declarationPropertyValueBlacklist, - "declaration-property-value-whitelist": declarationPropertyValueWhitelist, - "font-family-name-quotes": fontFamilyNameQuotes, - "font-family-no-duplicate-names": fontFamilyNoDuplicateNames, - "font-weight-notation": fontWeightNotation, - "function-blacklist": functionBlacklist, - "function-calc-no-unspaced-operator": functionCalcNoUnspacedOperator, - "function-comma-newline-after": functionCommaNewlineAfter, - "function-comma-newline-before": functionCommaNewlineBefore, - "function-comma-space-after": functionCommaSpaceAfter, - "function-comma-space-before": functionCommaSpaceBefore, - "function-linear-gradient-no-nonstandard-direction": functionLinearGradientNoNonstandardDirection, - "function-max-empty-lines": functionMaxEmptyLines, - "function-name-case": functionNameCase, - "function-parentheses-newline-inside": functionParenthesesNewlineInside, - "function-parentheses-space-inside": functionParenthesesSpaceInside, - "function-url-data-uris": functionUrlDataUris, - "function-url-no-scheme-relative": functionUrlNoSchemeRelative, - "function-url-quotes": functionUrlQuotes, - "function-url-scheme-whitelist": functionUrlSchemeWhitelist, - "function-whitelist": functionWhitelist, - "function-whitespace-after": functionWhitespaceAfter, - "indentation": indentation, // eslint-disable-line object-shorthand - "keyframe-declaration-no-important": keyframeDeclarationNoImportant, - "length-zero-no-unit": lengthZeroNoUnit, - "max-empty-lines": maxEmptyLines, - "max-line-length": maxLineLength, - "max-nesting-depth": maxNestingDepth, - "media-feature-colon-space-after": mediaFeatureColonSpaceAfter, - "media-feature-colon-space-before": mediaFeatureColonSpaceBefore, - "media-feature-name-blacklist": mediaFeatureNameBlacklist, - "media-feature-name-case": mediaFeatureNameCase, - "media-feature-name-no-unknown": mediaFeatureNameNoUnknown, - "media-feature-name-no-vendor-prefix": mediaFeatureNameNoVendorPrefix, - "media-feature-name-whitelist": mediaFeatureNameWhitelist, - "media-feature-no-missing-punctuation": mediaFeatureNoMissingPunctuation, - "media-feature-parentheses-space-inside": mediaFeatureParenthesesSpaceInside, - "media-feature-range-operator-space-after": mediaFeatureRangeOperatorSpaceAfter, - "media-feature-range-operator-space-before": mediaFeatureRangeOperatorSpaceBefore, - "media-query-list-comma-newline-after": mediaQueryListCommaNewlineAfter, - "media-query-list-comma-newline-before": mediaQueryListCommaNewlineBefore, - "media-query-list-comma-space-after": mediaQueryListCommaSpaceAfter, - "media-query-list-comma-space-before": mediaQueryListCommaSpaceBefore, - "no-browser-hacks": noBrowserHacks, - "no-descending-specificity": noDescendingSpecificity, - "no-duplicate-selectors": noDuplicateSelectors, - "no-empty-source": noEmptySource, - "no-eol-whitespace": noEolWhitespace, - "no-extra-semicolons": noExtraSemicolons, - "no-indistinguishable-colors": noIndistinguishableColors, - "no-invalid-double-slash-comments": noInvalidDoubleSlashComments, - "no-missing-end-of-source-newline": noMissingEndOfSourceNewline, - "no-unknown-animations": noUnknownAnimations, - "no-unsupported-browser-features": noSupportedBrowserFeatures, - "number-leading-zero": numberLeadingZero, - "number-max-precision": numberMaxPrecision, - "number-no-trailing-zeros": numberNoTrailingZeros, - "property-blacklist": propertyBlacklist, - "property-case": propertyCase, - "property-no-unknown": propertyNoUnknown, - "property-no-vendor-prefix": propertyNoVendorPrefix, - "property-whitelist": propertyWhitelist, - "root-no-standard-properties": rootNoStandardProperties, - "rule-nested-empty-line-before": ruleNestedEmptyLineBefore, - "rule-non-nested-empty-line-before": ruleNonNestedEmptyLineBefore, - "selector-attribute-brackets-space-inside": selectorAttributeBracketsSpaceInside, - "selector-attribute-operator-blacklist": selectorAttributeOperatorBlacklist, - "selector-attribute-operator-space-after": selectorAttributeOperatorSpaceAfter, - "selector-attribute-operator-space-before": selectorAttributeOperatorSpaceBefore, - "selector-attribute-operator-whitelist": selectorAttributeOperatorWhitelist, - "selector-attribute-quotes": selectorAttributeQuotes, - "selector-class-pattern": selectorClassPattern, - "selector-combinator-space-after": selectorCombinatorSpaceAfter, - "selector-combinator-space-before": selectorCombinatorSpaceBefore, - "selector-descendant-combinator-no-non-space": selectorDescendantCombinatorNoNonSpace, - "selector-id-pattern": selectorIdPattern, - "selector-list-comma-newline-after": selectorListCommaNewlineAfter, - "selector-list-comma-newline-before": selectorListCommaNewlineBefore, - "selector-list-comma-space-after": selectorListCommaSpaceAfter, - "selector-list-comma-space-before": selectorListCommaSpaceBefore, - "selector-max-compound-selectors": selectorMaxCompoundSelectors, - "selector-max-empty-lines": selectorMaxEmptyLines, - "selector-max-specificity": selectorMaxSpecificity, - "selector-nested-pattern": selectorNestedPattern, - "selector-no-attribute": selectorNoAttribute, - "selector-no-empty": selectorNoEmpty, - "selector-no-combinator": selectorNoCombinator, - "selector-no-id": selectorNoId, - "selector-no-qualifying-type": selectorNoQualifyingType, - "selector-no-type": selectorNoType, - "selector-no-universal": selectorNoUniversal, - "selector-no-vendor-prefix": selectorNoVendorPrefix, - "selector-pseudo-class-blacklist": selectorPseudoClassBlacklist, - "selector-pseudo-class-case": selectorPseudoClassCase, - "selector-pseudo-class-no-unknown": selectorPseudoClassNoUnknown, - "selector-pseudo-class-parentheses-space-inside": selectorPseudoClassParenthesesSpaceInside, - "selector-pseudo-class-whitelist": selectorPseudoClassWhitelist, - "selector-pseudo-element-case": selectorPseudoElementCase, - "selector-pseudo-element-colon-notation": selectorPseudoElementColonNotation, - "selector-pseudo-element-no-unknown": selectorPseudoElementNoUnknown, - "selector-root-no-composition": selectorRootNoComposition, - "selector-type-case": selectorTypeCase, - "selector-type-no-unknown": selectorTypeNoUnknown, - "shorthand-property-no-redundant-values": shorthandPropertyNoRedundantValues, - "string-no-newline": stringNoNewline, - "string-quotes": stringQuotes, - "stylelint-disable-reason": stylelintDisableReason, - "time-no-imperceptible": timeNoImperceptible, - "unit-blacklist": unitBlacklist, - "unit-case": unitCase, - "unit-no-unknown": unitNoUnknown, - "unit-whitelist": unitWhitelist, - "value-keyword-case": valueKeywordCase, - "value-list-comma-newline-after": valueListCommaNewlineAfter, - "value-list-comma-newline-before": valueListCommaNewlineBefore, - "value-list-comma-space-after": valueListCommaSpaceAfter, - "value-list-comma-space-before": valueListCommaSpaceBefore, - "value-list-max-empty-lines": valueListMaxEmptyLines, - "value-no-vendor-prefix": valueNoVendorPrefix, -} diff --git a/src/rules/keyframe-declaration-no-important/index.js b/src/rules/keyframe-declaration-no-important/index.js deleted file mode 100644 index 50ec7faf10..0000000000 --- a/src/rules/keyframe-declaration-no-important/index.js +++ /dev/null @@ -1,31 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "keyframe-declaration-no-important" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected !important", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkAtRules(/^(-(moz|webkit)-)?keyframes$/i, atRuleKeyframes => { - atRuleKeyframes.walkDecls(decl => { - if (!decl.important) { return } - report({ - message: messages.rejected, - node: decl, - word: "important", - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/media-feature-colon-space-after/index.js b/src/rules/media-feature-colon-space-after/index.js deleted file mode 100644 index c2a1cdbdcb..0000000000 --- a/src/rules/media-feature-colon-space-after/index.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - atRuleParamIndex, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "media-feature-colon-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected single space after \":\"", - rejectedAfter: () => "Unexpected whitespace after \":\"", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - mediaFeatureColonSpaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - }) - } -} - -export function mediaFeatureColonSpaceChecker({ locationChecker, root, result, checkedRuleName }) { - root.walkAtRules(/^media$/i, atRule => { - const { params } = atRule - - styleSearch({ source: params, target: ":" }, match => { - checkColon(params, match.startIndex, atRule) - }) - }) - - function checkColon(source, index, node) { - locationChecker({ source, index, err: m => - report({ - message: m, - node, - index: index + atRuleParamIndex(node), - result, - ruleName: checkedRuleName, - }), - }) - } -} diff --git a/src/rules/media-feature-colon-space-before/index.js b/src/rules/media-feature-colon-space-before/index.js deleted file mode 100644 index 3c1abf39d6..0000000000 --- a/src/rules/media-feature-colon-space-before/index.js +++ /dev/null @@ -1,35 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -import { mediaFeatureColonSpaceChecker } from "../media-feature-colon-space-after" - -export const ruleName = "media-feature-colon-space-before" - -export const messages = ruleMessages(ruleName, { - expectedBefore: () => "Expected single space before \":\"", - rejectedBefore: () => "Unexpected whitespace before \":\"", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - mediaFeatureColonSpaceChecker({ - root, - result, - locationChecker: checker.before, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/media-feature-name-blacklist/index.js b/src/rules/media-feature-name-blacklist/index.js deleted file mode 100644 index 74e45462b6..0000000000 --- a/src/rules/media-feature-name-blacklist/index.js +++ /dev/null @@ -1,49 +0,0 @@ -import { - atRuleParamIndex, - isCustomMediaQuery, - isRangeContextMediaFeature, - isStandardSyntaxMediaFeatureName, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, - } from "../../utils" -import { isString } from "lodash" -import mediaParser from "postcss-media-query-parser" - -export const ruleName = "media-feature-name-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (name) => `Unexpected media feature name "${name}"`, -}) - -export default function (blacklist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkAtRules(/^media$/i, atRule => { - mediaParser(atRule.params).walk(/^media-feature$/i, mediaFeatureNode => { - const { parent, sourceIndex, value } = mediaFeatureNode - - if (isRangeContextMediaFeature(parent.value) - || !isStandardSyntaxMediaFeatureName(value) - || isCustomMediaQuery(value) - ) { return } - - if (!matchesStringOrRegExp(value.toLowerCase(), blacklist)) { return } - - report({ - index: atRuleParamIndex(atRule) + sourceIndex, - message: messages.rejected(value), - node: atRule, - ruleName, - result, - }) - }) - }) - } -} diff --git a/src/rules/media-feature-name-case/index.js b/src/rules/media-feature-name-case/index.js deleted file mode 100644 index df546c513c..0000000000 --- a/src/rules/media-feature-name-case/index.js +++ /dev/null @@ -1,54 +0,0 @@ -import { - atRuleParamIndex, - isCustomMediaQuery, - isRangeContextMediaFeature, - isStandardSyntaxMediaFeatureName, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import mediaParser from "postcss-media-query-parser" - -export const ruleName = "media-feature-name-case" - -export const messages = ruleMessages(ruleName, { - expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "lower", - "upper", - ], - }) - if (!validOptions) { return } - - root.walkAtRules(/^media$/i, atRule => { - mediaParser(atRule.params).walk(/^media-feature$/i, mediaFeatureNode => { - const { parent, sourceIndex, value } = mediaFeatureNode - - if (isRangeContextMediaFeature(parent.value) - || !isStandardSyntaxMediaFeatureName(value) - || isCustomMediaQuery(value) - ) { return } - - const expectedFeatureName = expectation === "lower" - ? value.toLowerCase() - : value.toUpperCase() - - if (value === expectedFeatureName) { return } - - report({ - index: atRuleParamIndex(atRule) + sourceIndex, - message: messages.expected(value, expectedFeatureName), - node: atRule, - ruleName, - result, - }) - }) - }) - } -} diff --git a/src/rules/media-feature-name-no-unknown/index.js b/src/rules/media-feature-name-no-unknown/index.js deleted file mode 100644 index 92bc46e44d..0000000000 --- a/src/rules/media-feature-name-no-unknown/index.js +++ /dev/null @@ -1,57 +0,0 @@ -import { - atRuleParamIndex, - isCustomMediaQuery, - isRangeContextMediaFeature, - isStandardSyntaxMediaFeatureName, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import { mediaFeatureNames } from "../../reference/keywordSets" -import mediaParser from "postcss-media-query-parser" -import { vendor } from "postcss" - -export const ruleName = "media-feature-name-no-unknown" - -export const messages = ruleMessages(ruleName, { - rejected: (mediaFeatureName) => `Unexpected unknown media feature name "${mediaFeatureName}"`, -}) - -export default function (actual, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }, { - actual: options, - possible: { - ignoreMediaFeatureNames: [isString], - }, - optional: true, - }) - - if (!validOptions) { return } - - root.walkAtRules(/^media$/i, atRule => { - mediaParser(atRule.params).walk(/^media-feature$/i, mediaFeatureNode => { - const { parent, sourceIndex, value } = mediaFeatureNode - - if (isRangeContextMediaFeature(parent.value) - || !isStandardSyntaxMediaFeatureName(value) - || isCustomMediaQuery(value) - ) { return } - - if (optionsMatches(options, "ignoreMediaFeatureNames", value)) { return } - - if (vendor.prefix(value) || mediaFeatureNames.has(value.toLowerCase())) { return } - - report({ - index: atRuleParamIndex(atRule) + sourceIndex, - message: messages.rejected(value), - node: atRule, - ruleName, - result, - }) - }) - }) - } -} diff --git a/src/rules/media-feature-name-no-vendor-prefix/index.js b/src/rules/media-feature-name-no-vendor-prefix/index.js deleted file mode 100644 index ae2603c357..0000000000 --- a/src/rules/media-feature-name-no-vendor-prefix/index.js +++ /dev/null @@ -1,34 +0,0 @@ -import { - isAutoprefixable, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "media-feature-name-no-vendor-prefix" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected vendor-prefix", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkAtRules(/^media$/i, atRule => { - const { params } = atRule - if (!isAutoprefixable.mediaFeatureName(params)) { return } - const matches = atRule.toString().match(/[a-z-]+device-pixel-ratio/ig) - matches.forEach(match => { - report({ - message: messages.rejected, - node: atRule, - word: match, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/media-feature-name-whitelist/index.js b/src/rules/media-feature-name-whitelist/index.js deleted file mode 100644 index 329c2e8463..0000000000 --- a/src/rules/media-feature-name-whitelist/index.js +++ /dev/null @@ -1,49 +0,0 @@ -import { - atRuleParamIndex, - isCustomMediaQuery, - isRangeContextMediaFeature, - isStandardSyntaxMediaFeatureName, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, - } from "../../utils" -import { isString } from "lodash" -import mediaParser from "postcss-media-query-parser" - -export const ruleName = "media-feature-name-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (name) => `Unexpected media feature name "${name}"`, -}) - -export default function (whitelist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkAtRules(/^media$/i, atRule => { - mediaParser(atRule.params).walk(/^media-feature$/i, mediaFeatureNode => { - const { parent, sourceIndex, value } = mediaFeatureNode - - if (isRangeContextMediaFeature(parent.value) - || !isStandardSyntaxMediaFeatureName(value) - || isCustomMediaQuery(value) - ) { return } - - if (matchesStringOrRegExp(value.toLowerCase(), whitelist)) { return } - - report({ - index: atRuleParamIndex(atRule) + sourceIndex, - message: messages.rejected(value), - node: atRule, - ruleName, - result, - }) - }) - }) - } -} diff --git a/src/rules/media-query-list-comma-newline-before/index.js b/src/rules/media-query-list-comma-newline-before/index.js deleted file mode 100644 index 6e2c915e3e..0000000000 --- a/src/rules/media-query-list-comma-newline-before/index.js +++ /dev/null @@ -1,35 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { mediaQueryListCommaWhitespaceChecker } from "../media-query-list-comma-space-after" - -export const ruleName = "media-query-list-comma-newline-before" - -export const messages = ruleMessages(ruleName, { - expectedBefore: () => "Expected newline before \",\"", - expectedBeforeMultiLine: () => "Expected newline before \",\" in a multi-line list", - rejectedBeforeMultiLine: () => "Unexpected whitespace before \",\" in a multi-line list", -}) - -export default function (expectation) { - const checker = whitespaceChecker("newline", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], - }) - if (!validOptions) { return } - mediaQueryListCommaWhitespaceChecker({ - root, - result, - locationChecker: checker.beforeAllowingIndentation, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/media-query-list-comma-space-after/index.js b/src/rules/media-query-list-comma-space-after/index.js deleted file mode 100644 index ab2af64e75..0000000000 --- a/src/rules/media-query-list-comma-space-after/index.js +++ /dev/null @@ -1,60 +0,0 @@ -import { - atRuleParamIndex, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "media-query-list-comma-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected single space after \",\"", - rejectedAfter: () => "Unexpected whitespace after \",\"", - expectedAfterSingleLine: () => "Expected single space after \",\" in a single-line list", - rejectedAfterSingleLine: () => "Unexpected whitespace after \",\" in a single-line list", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], - }) - if (!validOptions) { return } - mediaQueryListCommaWhitespaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - }) - } -} - -export function mediaQueryListCommaWhitespaceChecker({ locationChecker, root, result, checkedRuleName }) { - root.walkAtRules(/^media$/i, atRule => { - const params = atRule.params - styleSearch({ source: params, target: "," }, match => { - checkComma(params, match.startIndex, atRule) - }) - }) - - function checkComma(source, index, node) { - locationChecker({ source, index, err: m => - report({ - message: m, - node, - index: index + atRuleParamIndex(node), - result, - ruleName: checkedRuleName, - }), - }) - } -} diff --git a/src/rules/no-empty-source/index.js b/src/rules/no-empty-source/index.js deleted file mode 100644 index 9d2600da63..0000000000 --- a/src/rules/no-empty-source/index.js +++ /dev/null @@ -1,27 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "no-empty-source" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected empty source", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - if (!(/^\s*$/).test(root.toString())) { return } - - report({ - message: messages.rejected, - node: root, - result, - ruleName, - }) - } -} diff --git a/src/rules/no-missing-end-of-source-newline/index.js b/src/rules/no-missing-end-of-source-newline/index.js deleted file mode 100644 index 86249a2f58..0000000000 --- a/src/rules/no-missing-end-of-source-newline/index.js +++ /dev/null @@ -1,29 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "no-missing-end-of-source-newline" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected missing end-of-source newline", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - const sourceCss = root.source.input.css - if (sourceCss === "" || sourceCss.slice(-1) === "\n") { return } - - report({ - message: messages.rejected, - node: root, - index: sourceCss.length - 1, - result, - ruleName, - }) - } -} diff --git a/src/rules/no-unknown-animations/index.js b/src/rules/no-unknown-animations/index.js deleted file mode 100644 index 758dd20e0e..0000000000 --- a/src/rules/no-unknown-animations/index.js +++ /dev/null @@ -1,47 +0,0 @@ -import { - declarationValueIndex, - findAnimationName, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { animationNameKeywords } from "../../reference/keywordSets" - -export const ruleName = "no-unknown-animations" - -export const messages = ruleMessages(ruleName, { - rejected: animationName => `Unexpected unknown animation name "${animationName}"`, -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - const declaredAnimations = new Set() - root.walkAtRules(/(-(moz|webkit)-)?keyframes/i, atRule => { - declaredAnimations.add(atRule.params) - }) - - root.walkDecls(decl => { - if (decl.prop.toLowerCase() === "animation" || decl.prop.toLowerCase() === "animation-name") { - const animationNames = findAnimationName(decl.value) - - if (animationNames.length === 0) { return } - - animationNames.forEach((animationNameNode) => { - if (animationNameKeywords.has(animationNameNode.value.toLowerCase())) { return } - if (declaredAnimations.has(animationNameNode.value)) { return } - - report({ - result, - ruleName, - message: messages.rejected(animationNameNode.value), - node: decl, - index: declarationValueIndex(decl) + animationNameNode.sourceIndex, - }) - }) - } - }) - } -} diff --git a/src/rules/number-max-precision/index.js b/src/rules/number-max-precision/index.js deleted file mode 100644 index 330416b7c4..0000000000 --- a/src/rules/number-max-precision/index.js +++ /dev/null @@ -1,63 +0,0 @@ -import { - atRuleParamIndex, - declarationValueIndex, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -import { isNumber } from "lodash" -import valueParser from "postcss-value-parser" - -export const ruleName = "number-max-precision" - -export const messages = ruleMessages(ruleName, { - expected: (number, precision) => `Expected "${number}" to be "${number.toFixed(precision)}"`, -}) - -export default function (precision) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: precision, - possible: [isNumber], - }) - if (!validOptions) { return } - - root.walkAtRules(atRule => { - if (atRule.name.toLowerCase() === "import") { return } - - check(atRule, atRule.params, atRuleParamIndex) - }) - - root.walkDecls(decl => - check(decl, decl.value, declarationValueIndex) - ) - - function check(node, value, getIndex) { - // Get out quickly if there are no periods - if (value.indexOf(".") === -1) { return } - - valueParser(value).walk(valueNode => { - // Ignore `url` function - if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { return false } - - // Ignore strings, comments, etc - if (valueNode.type !== "word") { return } - - const match = /\d*\.(\d+)/.exec(valueNode.value) - - if (match === null) { return } - - if (match[1].length <= precision) { return } - - report({ - result, - ruleName, - node, - index: getIndex(node) + valueNode.sourceIndex + match.index, - message: messages.expected(parseFloat(match[0]), precision), - }) - }) - } - } -} diff --git a/src/rules/number-no-trailing-zeros/index.js b/src/rules/number-no-trailing-zeros/index.js deleted file mode 100644 index 7051c6597b..0000000000 --- a/src/rules/number-no-trailing-zeros/index.js +++ /dev/null @@ -1,56 +0,0 @@ -import { - atRuleParamIndex, - declarationValueIndex, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import valueParser from "postcss-value-parser" - -export const ruleName = "number-no-trailing-zeros" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected trailing zero(s)", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkAtRules(atRule => { - if (atRule.name.toLowerCase() === "import") { return } - - check(atRule, atRule.params, atRuleParamIndex) - }) - - root.walkDecls(decl => - check(decl, decl.value, declarationValueIndex) - ) - - function check(node, value, getIndex) { - // Get out quickly if there are no periods - if (value.indexOf(".") === -1) { return } - - valueParser(value).walk(valueNode => { - // Ignore `url` function - if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { return false } - - // Ignore strings, comments, etc - if (valueNode.type !== "word") { return } - - const match = /(\.\d*)0+(?:\D|$)/.exec(valueNode.value) - - if (match === null) { return } - - report({ - message: messages.rejected, - node, - index: getIndex(node) + valueNode.sourceIndex + match.index + match[1].length, - result, - ruleName, - }) - }) - } - } -} diff --git a/src/rules/property-blacklist/index.js b/src/rules/property-blacklist/index.js deleted file mode 100644 index 05c18ab318..0000000000 --- a/src/rules/property-blacklist/index.js +++ /dev/null @@ -1,44 +0,0 @@ -import { - isCustomProperty, - isStandardSyntaxProperty, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import { vendor } from "postcss" - -export const ruleName = "property-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (property) => `Unexpected property "${property}"`, -}) - -function rule(blacklist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop } = decl - if (!isStandardSyntaxProperty(prop)) { return } - if (isCustomProperty(prop)) { return } - if (!matchesStringOrRegExp(vendor.unprefixed(prop), blacklist)) { return } - - report({ - message: messages.rejected(prop), - node: decl, - result, - ruleName, - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/property-case/index.js b/src/rules/property-case/index.js deleted file mode 100644 index 69d7b27d06..0000000000 --- a/src/rules/property-case/index.js +++ /dev/null @@ -1,42 +0,0 @@ -import { - isCustomProperty, - isStandardSyntaxProperty, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "property-case" - -export const messages = ruleMessages(ruleName, { - expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "lower", - "upper", - ], - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop } = decl - if (!isStandardSyntaxProperty(prop)) { return } - if (isCustomProperty(prop)) { return } - - const expectedProp = expectation === "lower" ? prop.toLowerCase() : prop.toUpperCase() - if (prop === expectedProp) { return } - - report({ - message: messages.expected(prop, expectedProp), - node: decl, - ruleName, - result, - }) - }) - } -} diff --git a/src/rules/property-no-unknown/index.js b/src/rules/property-no-unknown/index.js deleted file mode 100644 index f91bd555fe..0000000000 --- a/src/rules/property-no-unknown/index.js +++ /dev/null @@ -1,56 +0,0 @@ -import { - isCustomProperty, - isStandardSyntaxDeclaration, - isStandardSyntaxProperty, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" -import { all as properties } from "known-css-properties" -import { vendor } from "postcss" - -export const ruleName = "property-no-unknown" - -export const messages = ruleMessages(ruleName, { - rejected: (property) => `Unexpected unknown property "${property}"`, -}) - -export default function (actual, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }, { - actual: options, - possible: { - ignoreProperties: [_.isString], - checkPrefixed: _.isBoolean, - }, - optional: true, - }) - - if (!validOptions) { return } - - const shouldCheckPrefixed = _.get(options, "checkPrefixed") - - root.walkDecls(decl => { - const { prop } = decl - - if (!isStandardSyntaxProperty(prop)) { return } - if (!isStandardSyntaxDeclaration(decl)) { return } - if (isCustomProperty(prop)) { return } - - if (!shouldCheckPrefixed && vendor.prefix(prop)) { return } - - if (optionsMatches(options, "ignoreProperties", prop)) { return } - - if (properties.indexOf(prop.toLowerCase()) !== -1) { return } - - report({ - message: messages.rejected(prop), - node: decl, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/property-no-vendor-prefix/index.js b/src/rules/property-no-vendor-prefix/index.js deleted file mode 100644 index 60f3170ef7..0000000000 --- a/src/rules/property-no-vendor-prefix/index.js +++ /dev/null @@ -1,35 +0,0 @@ -import { - isAutoprefixable, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "property-no-vendor-prefix" - -export const messages = ruleMessages(ruleName, { - rejected: property => `Unexpected vendor-prefix "${property}"`, -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop } = decl - - // Make sure there's a vendor prefix, - // but this isn't a custom property - if (prop[0] !== "-" || prop[1] === "-") { return } - - if (!isAutoprefixable.property(prop)) { return } - report({ - message: messages.rejected(prop), - node: decl, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/property-whitelist/index.js b/src/rules/property-whitelist/index.js deleted file mode 100644 index 591c0680fa..0000000000 --- a/src/rules/property-whitelist/index.js +++ /dev/null @@ -1,44 +0,0 @@ -import { - isCustomProperty, - isStandardSyntaxProperty, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import { vendor } from "postcss" - -export const ruleName = "property-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (property) => `Unexpected property "${property}"`, -}) - -function rule(whitelist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop } = decl - if (!isStandardSyntaxProperty(prop)) { return } - if (isCustomProperty(prop)) { return } - if (matchesStringOrRegExp(vendor.unprefixed(prop), whitelist)) { return } - - report({ - message: messages.rejected(prop), - node: decl, - result, - ruleName, - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/root-no-standard-properties/index.js b/src/rules/root-no-standard-properties/index.js deleted file mode 100644 index 57d7bcd001..0000000000 --- a/src/rules/root-no-standard-properties/index.js +++ /dev/null @@ -1,60 +0,0 @@ -import { - isCustomProperty, - isStandardSyntaxProperty, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "root-no-standard-properties" - -export const messages = ruleMessages(ruleName, { - rejected: property => `Unexpected standard property "${property}"`, -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (rule.selector.toLowerCase().indexOf(":root") === -1) { return } - parseSelector(rule.selector, result, rule, checkSelector) - - function checkSelector(selectorAST) { - if (ignoreRule(selectorAST)) { return } - - rule.each(function (node) { - if (node.type !== "decl") { return } - - const { prop } = node - if (!isStandardSyntaxProperty(prop)) { return } - if (isCustomProperty(prop)) { return } - - report({ - message: messages.rejected(prop), - node, - result, - ruleName, - }) - }) - } - }) - } -} - -function ignoreRule(selectorAST) { - let ignore = false - selectorAST.walk(selectorNode => { - // ignore `:root` selector inside a `:not()` selector - if (selectorNode.value - && selectorNode.value.toLowerCase() === ":root" - && selectorNode.parent.parent.value - && selectorNode.parent.parent.value.toLowerCase() === ":not" - ) { - ignore = true - } - }) - return ignore -} diff --git a/src/rules/rule-nested-empty-line-before/index.js b/src/rules/rule-nested-empty-line-before/index.js deleted file mode 100644 index d0a13bda59..0000000000 --- a/src/rules/rule-nested-empty-line-before/index.js +++ /dev/null @@ -1,50 +0,0 @@ -import { - isStandardSyntaxRule, - ruleMessages, - validateOptions, -} from "../../utils" - -import { checkRuleEmptyLineBefore } from "../rule-non-nested-empty-line-before" - -export const ruleName = "rule-nested-empty-line-before" - -export const messages = ruleMessages(ruleName, { - expected: "Expected empty line before nested rule", - rejected: "Unexpected empty line before nested rule", -}) - -export default function (expectation, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - "always-multi-line", - "never-multi-line", - ], - }, { - actual: options, - possible: { - ignore: [ - "after-comment", - ], - except: [ - "first-nested", - "after-comment", - ], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - - // Only attend to nested rule sets - if (rule.parent === root) { return } - - checkRuleEmptyLineBefore({ rule, expectation, options, result, messages, checkedRuleName: ruleName }) - }) - } -} diff --git a/src/rules/rule-non-nested-empty-line-before/index.js b/src/rules/rule-non-nested-empty-line-before/index.js deleted file mode 100644 index e9ebdd118c..0000000000 --- a/src/rules/rule-non-nested-empty-line-before/index.js +++ /dev/null @@ -1,92 +0,0 @@ -import { - hasEmptyLine, - isSingleLineString, - isStandardSyntaxRule, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "rule-non-nested-empty-line-before" - -export const messages = ruleMessages(ruleName, { - expected: "Expected empty line before non-nested rule", - rejected: "Unexpected empty line before non-nested rule", -}) - -export default function (expectation, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - "always-multi-line", - "never-multi-line", - ], - }, { - actual: options, - possible: { - ignore: ["after-comment"], - except: ["after-single-line-comment"], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - - // Ignore nested rule sets - if (rule.parent !== root) { return } - - // Ignore the first node - if (rule === root.first) { return } - - checkRuleEmptyLineBefore({ rule, expectation, options, result, messages, checkedRuleName: ruleName }) - }) - } -} - -export function checkRuleEmptyLineBefore({ rule, expectation, options, result, messages, checkedRuleName }) { - let expectEmptyLineBefore = (expectation.indexOf("always") !== -1) ? true : false - - // Optionally ignore the expectation if a comment precedes this node - if (optionsMatches(options, "ignore", "after-comment") - && rule.prev() && rule.prev().type === "comment") { return } - - // Ignore if the expectation is for multiple and the rule is single-line - if (expectation.indexOf("multi-line") !== -1 - && isSingleLineString(rule.toString())) { return } - - // Optionally reverse the expectation for the first nested node - if (optionsMatches(options, "except", "first-nested") - && rule === rule.parent.first) { - expectEmptyLineBefore = !expectEmptyLineBefore - } - - // Optionally reverse the expectation for single line comments - if ( - optionsMatches(options, "except", "after-single-line-comment") - && rule.prev() - && rule.prev().type === "comment" - && isSingleLineString(rule.prev().toString()) - ) { - expectEmptyLineBefore = !expectEmptyLineBefore - } - - const hasEmptyLineBefore = hasEmptyLine(rule.raws.before) - - // Return if the expectation is met - if (expectEmptyLineBefore === hasEmptyLineBefore) { return } - - const message = expectEmptyLineBefore ? messages.expected : messages.rejected - - report({ - message, - node: rule, - result, - ruleName: checkedRuleName, - }) -} diff --git a/src/rules/selector-attribute-operator-blacklist/index.js b/src/rules/selector-attribute-operator-blacklist/index.js deleted file mode 100644 index 2caf409e6a..0000000000 --- a/src/rules/selector-attribute-operator-blacklist/index.js +++ /dev/null @@ -1,52 +0,0 @@ -import { - isStandardSyntaxRule, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" - -export const ruleName = "selector-attribute-operator-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (operator) => `Unexpected operator "${operator}"`, -}) - -function rule(blacklistInput) { - const blacklist = [].concat(blacklistInput) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (rule.selector.indexOf("[") === -1 - || rule.selector.indexOf("=") === -1 - ) { return } - - parseSelector(rule.selector, result, rule, selectorTree => { - selectorTree.walkAttributes(attributeNode => { - const operator = attributeNode.operator - - if (!operator || (operator && blacklist.indexOf(operator) === -1)) { return } - - report({ - message: messages.rejected(operator), - node: rule, - index: attributeNode.attribute.length + 1, - result, - ruleName, - }) - }) - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/selector-attribute-operator-space-after/index.js b/src/rules/selector-attribute-operator-space-after/index.js deleted file mode 100644 index 35002e4839..0000000000 --- a/src/rules/selector-attribute-operator-space-after/index.js +++ /dev/null @@ -1,88 +0,0 @@ -import { - isStandardSyntaxRule, - parseSelector, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "selector-attribute-operator-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: (operator) => `Expected single space after "${operator}"`, - rejectedAfter: (operator) => `Unexpected whitespace after "${operator}"`, -}) - -export default function (expectation) { - return (root, result) => { - const checker = whitespaceChecker("space", expectation, messages) - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - selectorAttributeOperatorSpaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - checkBeforeOperator: false, - }) - } -} - -export function selectorAttributeOperatorSpaceChecker({ - locationChecker, - root, - result, - checkedRuleName, - checkBeforeOperator, -}) { - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (rule.selector.indexOf("[") === -1 - || rule.selector.indexOf("=") === -1 - ) { return } - - parseSelector(rule.selector, result, rule, selectorTree => { - selectorTree.walkAttributes(attributeNode => { - const operator = attributeNode.operator - - if (!operator) { return } - - const attributeNodeString = attributeNode.toString() - - styleSearch({ source: attributeNodeString, target: operator }, match => { - const index = checkBeforeOperator ? match.startIndex : match.endIndex - 1 - checkOperator( - attributeNodeString, - index, - rule, - attributeNode.sourceIndex, - operator - ) - }) - }) - }) - - function checkOperator(source, index, node, attributeIndex, operator) { - locationChecker({ - source, - index, - err: (m) => report({ - message: m.replace(checkBeforeOperator ? operator[0] : operator[operator.length - 1], operator), - node, - index: attributeIndex + index, - result, - ruleName: checkedRuleName, - }), - }) - } - }) -} diff --git a/src/rules/selector-attribute-operator-space-before/index.js b/src/rules/selector-attribute-operator-space-before/index.js deleted file mode 100644 index f79cc2bd1a..0000000000 --- a/src/rules/selector-attribute-operator-space-before/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -import { selectorAttributeOperatorSpaceChecker } from "../selector-attribute-operator-space-after" - -export const ruleName = "selector-attribute-operator-space-before" - -export const messages = ruleMessages(ruleName, { - expectedBefore: (operator) => `Expected single space before "${operator}"`, - rejectedBefore: (operator) => `Unexpected whitespace before "${operator}"`, -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - selectorAttributeOperatorSpaceChecker({ - root, - result, - locationChecker: checker.before, - checkedRuleName: ruleName, - checkBeforeOperator: true, - }) - } -} diff --git a/src/rules/selector-attribute-operator-whitelist/index.js b/src/rules/selector-attribute-operator-whitelist/index.js deleted file mode 100644 index 1c0ffeec2e..0000000000 --- a/src/rules/selector-attribute-operator-whitelist/index.js +++ /dev/null @@ -1,52 +0,0 @@ -import { - isStandardSyntaxRule, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" - -export const ruleName = "selector-attribute-operator-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (operator) => `Unexpected operator "${operator}"`, -}) - -function rule(whitelistInput) { - const whitelist = [].concat(whitelistInput) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (rule.selector.indexOf("[") === -1 - || rule.selector.indexOf("=") === -1 - ) { return } - - parseSelector(rule.selector, result, rule, selectorTree => { - selectorTree.walkAttributes(attributeNode => { - const operator = attributeNode.operator - - if (!operator || (operator && whitelist.indexOf(operator) !== -1)) { return } - - report({ - message: messages.rejected(operator), - node: rule, - index: attributeNode.attribute.length + 1, - result, - ruleName, - }) - }) - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/selector-attribute-quotes/index.js b/src/rules/selector-attribute-quotes/index.js deleted file mode 100644 index f6b4e7dd50..0000000000 --- a/src/rules/selector-attribute-quotes/index.js +++ /dev/null @@ -1,66 +0,0 @@ -import { - isStandardSyntaxRule, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-attribute-quotes" - -export const messages = ruleMessages(ruleName, { - expected: (value) => `Expected quotes around "${value}"`, - rejected: (value) => `Unexpected quotes around "${value}"`, -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (rule.selector.indexOf("[") === -1 - || rule.selector.indexOf("=") === -1 - ) { return } - - parseSelector(rule.selector, result, rule, selectorTree => { - selectorTree.walkAttributes(attributeNode => { - if (!attributeNode.operator) { return } - - const attributeSelectorString = attributeNode.toString() - - if (!attributeNode.quoted && expectation === "always") { - complain( - messages.expected(attributeNode.raws.unquoted), - attributeNode.sourceIndex + attributeSelectorString.indexOf(attributeNode.value) - ) - } - - if (attributeNode.quoted && expectation === "never") { - complain( - messages.rejected(attributeNode.raws.unquoted), - attributeNode.sourceIndex + attributeSelectorString.indexOf(attributeNode.value) - ) - } - }) - }) - - function complain(message, index) { - report({ - message, - index, - result, - ruleName, - node: rule, - }) - } - }) - } -} diff --git a/src/rules/selector-combinator-space-after/index.js b/src/rules/selector-combinator-space-after/index.js deleted file mode 100644 index be6707a3a6..0000000000 --- a/src/rules/selector-combinator-space-after/index.js +++ /dev/null @@ -1,73 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import _ from "lodash" -import { nonSpaceCombinators } from "../../reference/punctuationSets" -import styleSearch from "style-search" - -export const ruleName = "selector-combinator-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: combinator => `Expected single space after "${combinator}"`, - rejectedAfter: combinator => `Unexpected whitespace after "${combinator}"`, -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - selectorCombinatorSpaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - }) - } -} - -export function selectorCombinatorSpaceChecker({ locationChecker, root, result, checkedRuleName }) { - root.walkRules(rule => { - // Check each selector individually, instead of all as one string, - // in case some that aren't the first begin with combinators (nesting syntax) - rule.selectors.forEach(selector => { - styleSearch({ - source: selector, - target: _.toArray(nonSpaceCombinators), - parentheticals: "skip", - }, match => { - const { endIndex, startIndex, target } = match - - // Catch ~= in attribute selectors - if (target === "~" && selector[endIndex] === "=") { return } - - // Catch escaped combinator-like character - if (selector[startIndex - 1] === "\\") { return } - - check(selector, startIndex, rule) - }) - }) - }) - - function check(source, index, node) { - locationChecker({ source, index, err: m => - report({ - message: m, - node, - index, - result, - ruleName: checkedRuleName, - }), - }) - } -} diff --git a/src/rules/selector-combinator-space-before/index.js b/src/rules/selector-combinator-space-before/index.js deleted file mode 100644 index 4822708c50..0000000000 --- a/src/rules/selector-combinator-space-before/index.js +++ /dev/null @@ -1,35 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -import { selectorCombinatorSpaceChecker } from "../selector-combinator-space-after" - -export const ruleName = "selector-combinator-space-before" - -export const messages = ruleMessages(ruleName, { - expectedBefore: combinator => `Expected single space before "${combinator}"`, - rejectedBefore: combinator => `Unexpected whitespace before "${combinator}"`, -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - ], - }) - if (!validOptions) { return } - - selectorCombinatorSpaceChecker({ - root, - result, - locationChecker: checker.before, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/selector-descendant-combinator-no-non-space/index.js b/src/rules/selector-descendant-combinator-no-non-space/index.js deleted file mode 100644 index 5012485e55..0000000000 --- a/src/rules/selector-descendant-combinator-no-non-space/index.js +++ /dev/null @@ -1,44 +0,0 @@ -import { - isStandardSyntaxRule, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { nonSpaceCombinators } from "../../reference/punctuationSets" - -export const ruleName = "selector-descendant-combinator-no-non-space" - -export const messages = ruleMessages(ruleName, { - rejected: nonSpaceCharacter => `Unexpected "${nonSpaceCharacter}"`, -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - - const { selector } = rule - - parseSelector(selector, result, rule, fullSelector => { - fullSelector.walkCombinators(combinatorNode => { - const { value } = combinatorNode - - if (nonSpaceCombinators.has(value)) { return } - if (value === " ") { return } - - report({ - result, - ruleName, - message: messages.rejected(value), - node: rule, - index: combinatorNode.sourceIndex, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-id-pattern/index.js b/src/rules/selector-id-pattern/index.js deleted file mode 100644 index 6334cedadd..0000000000 --- a/src/rules/selector-id-pattern/index.js +++ /dev/null @@ -1,54 +0,0 @@ -import { - isRegExp, - isString, -} from "lodash" -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-id-pattern" - -export const messages = ruleMessages(ruleName, { - expected: selectorValue => `Expected id selector "#${selectorValue}" to match specified pattern`, -}) - -export default function (pattern) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: pattern, - possible: [ isRegExp, isString ], - }) - if (!validOptions) { return } - - const normalizedPattern = isString(pattern) ? new RegExp(pattern) : pattern - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - - const { selector } = rule - if (!isStandardSyntaxSelector(selector)) { return } - - parseSelector(selector, result, rule, fullSelector => { - fullSelector.walk(selectorNode => { - if (selectorNode.type !== "id") { return } - const { value, sourceIndex } = selectorNode - - if (normalizedPattern.test(value)) { return } - - report({ - result, - ruleName, - message: messages.expected(value), - node: rule, - index: sourceIndex, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-list-comma-newline-after/index.js b/src/rules/selector-list-comma-newline-after/index.js deleted file mode 100644 index e8cde64f33..0000000000 --- a/src/rules/selector-list-comma-newline-after/index.js +++ /dev/null @@ -1,68 +0,0 @@ -import { - isStandardSyntaxRule, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "selector-list-comma-newline-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected newline after \",\"", - expectedAfterMultiLine: () => "Expected newline after \",\" in a multi-line list", - rejectedAfterMultiLine: () => "Unexpected whitespace after \",\" in a multi-line list", -}) - -export default function (expectation) { - const checker = whitespaceChecker("newline", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - // Get raw selector so we can allow end-of-line comments, e.g. - // a, /* comment */ - // b {} - const selector = (rule.raws.selector) ? rule.raws.selector.raw : rule.selector - styleSearch({ - source: selector, - target: ",", - functionArguments: "skip", - }, match => { - const nextThreeChars = selector.substr(match.endIndex, 3) - - // If there's a // comment, that means there has to be a newline - // ending the comment so we're fine - if (nextThreeChars === " //") { return } - - // If there is a space and then a comment begins, look for the newline - // after that comment - const indextoCheckAfter = (nextThreeChars === " /*") - ? selector.indexOf("*/", match.endIndex) + 1 - : match.startIndex - checker.afterOneOnly({ - source: selector, - index: indextoCheckAfter, - err: m => - report({ - message: m, - node: rule, - index: match.startIndex, - result, - ruleName, - }), - }) - }) - }) - } -} diff --git a/src/rules/selector-list-comma-newline-before/index.js b/src/rules/selector-list-comma-newline-before/index.js deleted file mode 100644 index 1e0f300538..0000000000 --- a/src/rules/selector-list-comma-newline-before/index.js +++ /dev/null @@ -1,37 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" - -import { selectorListCommaWhitespaceChecker } from "../selector-list-comma-space-after" - -export const ruleName = "selector-list-comma-newline-before" - -export const messages = ruleMessages(ruleName, { - expectedBefore: () => "Expected newline before \",\"", - expectedBeforeMultiLine: () => "Expected newline before \",\" in a multi-line list", - rejectedBeforeMultiLine: () => "Unexpected whitespace before \",\" in a multi-line list", -}) - -export default function (expectation) { - const checker = whitespaceChecker("newline", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], - }) - if (!validOptions) { return } - - selectorListCommaWhitespaceChecker({ - root, - result, - locationChecker: checker.beforeAllowingIndentation, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/selector-list-comma-space-after/index.js b/src/rules/selector-list-comma-space-after/index.js deleted file mode 100644 index 03a0d0a46b..0000000000 --- a/src/rules/selector-list-comma-space-after/index.js +++ /dev/null @@ -1,66 +0,0 @@ -import { - isStandardSyntaxRule, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "selector-list-comma-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected single space after \",\"", - rejectedAfter: () => "Unexpected whitespace after \",\"", - expectedAfterSingleLine: () => "Expected single space after \",\" in a single-line list", - rejectedAfterSingleLine: () => "Unexpected whitespace after \",\" in a single-line list", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], - }) - if (!validOptions) { return } - - selectorListCommaWhitespaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - }) - } -} - -export function selectorListCommaWhitespaceChecker({ locationChecker, root, result, checkedRuleName }) { - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const selector = rule.selector - styleSearch({ - source: selector, - target: ",", - functionArguments: "skip", - }, match => { - checkDelimiter(selector, match.startIndex, rule) - }) - }) - - function checkDelimiter(source, index, node) { - locationChecker({ source, index, err: m => - report({ - message: m, - node, - index, - result, - ruleName: checkedRuleName, - }), - }) - } -} diff --git a/src/rules/selector-max-empty-lines/index.js b/src/rules/selector-max-empty-lines/index.js deleted file mode 100644 index eea49f3800..0000000000 --- a/src/rules/selector-max-empty-lines/index.js +++ /dev/null @@ -1,53 +0,0 @@ -import { - isNumber, - repeat, -} from "lodash" -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "selector-max-empty-lines" - -export const messages = ruleMessages(ruleName, { - expected: max => `Expected no more than ${max} empty line(s)`, -}) - -export default function (max) { - const maxAdjacentNewlines = max + 1 - - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: max, - possible: isNumber, - }) - if (!validOptions) { return } - - root.walkRules(rule => { - const selector = (rule.raws.selector) ? rule.raws.selector.raw : rule.selector - const repeatLFNewLines = repeat("\n", maxAdjacentNewlines) - const repeatCRLFNewLines = repeat("\r\n", maxAdjacentNewlines) - - styleSearch({ source: selector, target: "\n" }, match => { - if ( - selector.substr(match.startIndex + 1, maxAdjacentNewlines) === repeatLFNewLines - || selector.substr(match.startIndex + 1, maxAdjacentNewlines * 2) === repeatCRLFNewLines - ) { - // Put index at `\r` if it's CRLF, otherwise leave it at `\n` - let index = match.startIndex - if (selector[index - 1] === "\r") { index -= 1 } - - report({ - message: messages.expected(max), - node: rule, - index, - result, - ruleName, - }) - } - }) - }) - } -} diff --git a/src/rules/selector-nested-pattern/index.js b/src/rules/selector-nested-pattern/index.js deleted file mode 100644 index c733edb051..0000000000 --- a/src/rules/selector-nested-pattern/index.js +++ /dev/null @@ -1,46 +0,0 @@ -import { - isRegExp, - isString, -} from "lodash" -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-nested-pattern" - -export const messages = ruleMessages(ruleName, { - expected: selector => `Expected nested selector "${selector}" to match specified pattern`, -}) - -export default function (pattern) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: pattern, - possible: [ isRegExp, isString ], - }) - if (!validOptions) { return } - - const normalizedPattern = isString(pattern) ? new RegExp(pattern) : pattern - - root.walkRules(rule => { - if (rule.parent.type !== "rule") { return } - if (!isStandardSyntaxRule(rule)) { return } - - const { selector } = rule - if (!isStandardSyntaxSelector(selector)) { return } - - if (normalizedPattern.test(selector)) { return } - - report({ - result, - ruleName, - message: messages.expected(selector), - node: rule, - }) - }) - } -} diff --git a/src/rules/selector-no-attribute/index.js b/src/rules/selector-no-attribute/index.js deleted file mode 100644 index d0376e771a..0000000000 --- a/src/rules/selector-no-attribute/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-no-attribute" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected attribute selector", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const { selector } = rule - if (!isStandardSyntaxSelector(selector)) { return } - parseSelector(selector, result, rule, selectorAST => { - selectorAST.walkAttributes(attribute => { - report({ - message: messages.rejected, - node: rule, - index: attribute.sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-no-combinator/index.js b/src/rules/selector-no-combinator/index.js deleted file mode 100644 index 6796048832..0000000000 --- a/src/rules/selector-no-combinator/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-no-combinator" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected combinator", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const { selector } = rule - if (!isStandardSyntaxSelector(selector)) { return } - parseSelector(selector, result, rule, selectorAST => { - selectorAST.walkCombinators(combinator => { - report({ - message: messages.rejected, - node: rule, - index: combinator.sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-no-empty/index.js b/src/rules/selector-no-empty/index.js deleted file mode 100644 index bee80b3ec9..0000000000 --- a/src/rules/selector-no-empty/index.js +++ /dev/null @@ -1,37 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-no-empty" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected empty selector", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - let index = 0 - - rule.selector.split(",").forEach(item => { - index += item.length + 1 - - if (item.trim() !== "") { return } - - report({ - message: messages.rejected, - node: rule, - index: index - 1, - ruleName, - result, - }) - }) - }) - } -} - diff --git a/src/rules/selector-no-id/index.js b/src/rules/selector-no-id/index.js deleted file mode 100644 index 7fb06b4bc3..0000000000 --- a/src/rules/selector-no-id/index.js +++ /dev/null @@ -1,42 +0,0 @@ -import { - isKeyframeRule, - isStandardSyntaxRule, - isStandardSyntaxSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-no-id" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected id selector", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - if (isKeyframeRule(rule)) { return } - const { selector } = rule - if (!isStandardSyntaxSelector(selector)) { return } - parseSelector(selector, result, rule, selectorAST => { - selectorAST.walkIds(idNode => { - if (idNode.parent.parent.type === "pseudo") { return } - - report({ - message: messages.rejected, - node: rule, - index: idNode.sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-no-type/index.js b/src/rules/selector-no-type/index.js deleted file mode 100644 index f1b2127cc4..0000000000 --- a/src/rules/selector-no-type/index.js +++ /dev/null @@ -1,96 +0,0 @@ -import { - get, - isString, -} from "lodash" -import { - isKeyframeSelector, - isStandardSyntaxRule, - isStandardSyntaxSelector, - isStandardSyntaxTypeSelector, - optionsMatches, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import resolveNestedSelector from "postcss-resolve-nested-selector" - -export const ruleName = "selector-no-type" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected type selector", -}) - -export default function (on, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual: on }, { - actual: options, - possible: { - ignore: [ "descendant", "compounded" ], - ignoreTypes: [isString], - }, - optional: true, - }) - if (!validOptions) { return } - - const ignoreDescendant = optionsMatches(options, "ignore", "descendant") - const ignoreCompounded = optionsMatches(options, "ignore", "compounded") - - root.walkRules(rule => { - const { selector, selectors } = rule - - if (!isStandardSyntaxRule(rule)) { return } - if (!isStandardSyntaxSelector(selector)) { return } - if (selectors.some(s => isKeyframeSelector(s))) { return } - - if (ignoreDescendant) { - // Resolve each selector within the list before checking - selectors.forEach(selector => { - resolveNestedSelector(selector, rule).forEach(selector => { - checkSelector(selector, rule) - }) - }) - } else { - checkSelector(selector, rule) - } - }) - - function checkSelector(selector, rule) { - parseSelector(selector, result, rule, selectorAST => { - selectorAST.walkTags(tag => { - if (!isStandardSyntaxTypeSelector(tag)) { return } - - if (optionsMatches(options, "ignoreTypes", tag.value)) { return } - - if (ignoreDescendant && hasCombinatorBefore(tag)) { return } - - if (ignoreCompounded && isCompounded(tag)) { return } - - report({ - message: messages.rejected, - node: rule, - index: tag.sourceIndex, - ruleName, - result, - }) - }) - }) - } - } -} - -function hasCombinatorBefore(node) { - return node.parent.nodes.slice(0, node.parent.nodes.indexOf(node)) - .some(isCombinator) -} - -function isCompounded(node) { - if (node.prev() && !isCombinator(node.prev())) { return true } - if (node.next() && !isCombinator(node.next())) { return true } - return false -} - -function isCombinator(node) { - if (!node) return false - return get(node, "type") === "combinator" -} diff --git a/src/rules/selector-no-universal/index.js b/src/rules/selector-no-universal/index.js deleted file mode 100644 index bfc2fc9d0a..0000000000 --- a/src/rules/selector-no-universal/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-no-universal" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected universal selector", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const { selector } = rule - if (!isStandardSyntaxSelector(selector)) { return } - parseSelector(selector, result, rule, selectorAST => { - selectorAST.walkUniversals(universal => { - report({ - message: messages.rejected, - node: rule, - index: universal.sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-no-vendor-prefix/index.js b/src/rules/selector-no-vendor-prefix/index.js deleted file mode 100644 index 0449f10a1f..0000000000 --- a/src/rules/selector-no-vendor-prefix/index.js +++ /dev/null @@ -1,41 +0,0 @@ -import { - isAutoprefixable, - isStandardSyntaxRule, - isStandardSyntaxSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-no-vendor-prefix" - -export const messages = ruleMessages(ruleName, { - rejected: selector => `Unexpected vendor-prefix "${selector}"`, -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const { selector } = rule - if (!isStandardSyntaxSelector(selector)) { return } - parseSelector(selector, result, rule, selectorTree => { - selectorTree.walkPseudos(pseudoNode => { - if (isAutoprefixable.selector(pseudoNode.value)) { - report({ - result, - ruleName, - message: messages.rejected(pseudoNode.value), - node: rule, - index: rule.raws.before.length + pseudoNode.sourceIndex, - }) - } - }) - }) - }) - } -} diff --git a/src/rules/selector-pseudo-class-blacklist/index.js b/src/rules/selector-pseudo-class-blacklist/index.js deleted file mode 100644 index 983cecc6f7..0000000000 --- a/src/rules/selector-pseudo-class-blacklist/index.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - isStandardSyntaxSelector, - matchesStringOrRegExp, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import { vendor } from "postcss" - -export const ruleName = "selector-pseudo-class-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (selector) => `Unexpected pseudo-class "${selector}"`, -}) - -function rule(blacklist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - const { selector } = rule - - if (!isStandardSyntaxSelector(selector)) { return } - if (selector.indexOf(":") === -1) { return } - - parseSelector(selector, result, rule, selectorTree => { - selectorTree.walkPseudos(pseudoNode => { - const { value } = pseudoNode - - // Ignore pseudo-elements - if (value.slice(0, 2) === "::") { return } - - const name = value.slice(1) - - if (!matchesStringOrRegExp(vendor.unprefixed(name).toLowerCase(), blacklist)) { return } - - report({ - index: pseudoNode.sourceIndex, - message: messages.rejected(name), - node: rule, - result, - ruleName, - }) - }) - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/selector-pseudo-class-case/index.js b/src/rules/selector-pseudo-class-case/index.js deleted file mode 100644 index d6c21bf996..0000000000 --- a/src/rules/selector-pseudo-class-case/index.js +++ /dev/null @@ -1,64 +0,0 @@ -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { levelOneAndTwoPseudoElements } from "../../reference/keywordSets" - -export const ruleName = "selector-pseudo-class-case" - -export const messages = ruleMessages(ruleName, { - expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "lower", - "upper", - ], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const selector = rule.selector - const startIndexPseudo = selector.indexOf(":") - - if (startIndexPseudo === -1) { return } - - parseSelector(selector, result, rule, selectorTree => { - selectorTree.walkPseudos(pseudoNode => { - const pseudo = pseudoNode.value - - if (!isStandardSyntaxSelector(pseudo)) { return } - - if (pseudo.indexOf("::") !== -1 - || levelOneAndTwoPseudoElements.has(pseudo.toLowerCase().slice(1)) - ) { - return - } - - const expectedPseudo = expectation === "lower" - ? pseudo.toLowerCase() - : pseudo.toUpperCase() - - if (pseudo === expectedPseudo) { return } - - report({ - message: messages.expected(pseudo, expectedPseudo), - node: rule, - index: pseudoNode.sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-pseudo-class-no-unknown/index.js b/src/rules/selector-pseudo-class-no-unknown/index.js deleted file mode 100644 index e6b00a4d45..0000000000 --- a/src/rules/selector-pseudo-class-no-unknown/index.js +++ /dev/null @@ -1,71 +0,0 @@ -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - optionsMatches, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { - pseudoClasses, - pseudoElements, -} from "../../reference/keywordSets" -import { isString } from "lodash" -import { vendor } from "postcss" - -export const ruleName = "selector-pseudo-class-no-unknown" - -export const messages = ruleMessages(ruleName, { - rejected: (selector) => `Unexpected unknown pseudo-class selector "${selector}"`, -}) - -export default function (actual, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }, { - actual: options, - possible: { - ignorePseudoClasses: [isString], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const { selector } = rule - - // Return early before parse if no pseudos for performance - if (selector.indexOf(":") === -1) { return } - - parseSelector(selector, result, rule, selectorTree => { - selectorTree.walkPseudos(pseudoNode => { - const { value } = pseudoNode - - if (!isStandardSyntaxSelector(value)) { return } - - // Ignore pseudo-elements - if (value.slice(0, 2) === "::") { return } - - if (optionsMatches(options, "ignorePseudoClasses", pseudoNode.value.slice(1))) { return } - - const name = value.slice(1) - - if ( - vendor.prefix(name) - || pseudoClasses.has(name.toLowerCase()) - || pseudoElements.has(name.toLowerCase()) - ) { return } - - report({ - message: messages.rejected(value), - node: rule, - index: pseudoNode.sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-pseudo-class-whitelist/index.js b/src/rules/selector-pseudo-class-whitelist/index.js deleted file mode 100644 index 9e156d493c..0000000000 --- a/src/rules/selector-pseudo-class-whitelist/index.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - isStandardSyntaxSelector, - matchesStringOrRegExp, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import { vendor } from "postcss" - -export const ruleName = "selector-pseudo-class-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (selector) => `Unexpected pseudo-class "${selector}"`, -}) - -function rule(whitelist) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [isString], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - const { selector } = rule - - if (!isStandardSyntaxSelector(selector)) { return } - if (selector.indexOf(":") === -1) { return } - - parseSelector(selector, result, rule, selectorTree => { - selectorTree.walkPseudos(pseudoNode => { - const { value } = pseudoNode - - // Ignore pseudo-elements - if (value.slice(0, 2) === "::") { return } - - const name = value.slice(1) - - if (matchesStringOrRegExp(vendor.unprefixed(name).toLowerCase(), whitelist)) { return } - - report({ - index: pseudoNode.sourceIndex, - message: messages.rejected(name), - node: rule, - result, - ruleName, - }) - }) - }) - }) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/selector-pseudo-element-case/index.js b/src/rules/selector-pseudo-element-case/index.js deleted file mode 100644 index b0ff47002c..0000000000 --- a/src/rules/selector-pseudo-element-case/index.js +++ /dev/null @@ -1,64 +0,0 @@ -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { levelOneAndTwoPseudoElements } from "../../reference/keywordSets" - -export const ruleName = "selector-pseudo-element-case" - -export const messages = ruleMessages(ruleName, { - expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "lower", - "upper", - ], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const selector = rule.selector - const startIndexPseudoElement = selector.indexOf(":") - - if (startIndexPseudoElement === -1) { return } - - parseSelector(selector, result, rule, selectorTree => { - selectorTree.walkPseudos(pseudoNode => { - const pseudoElement = pseudoNode.value - - if (!isStandardSyntaxSelector(pseudoElement)) { return } - - if (pseudoElement.indexOf("::") === -1 - && !levelOneAndTwoPseudoElements.has(pseudoElement.toLowerCase().slice(1)) - ) { - return - } - - const expectedPseudoElement = expectation === "lower" - ? pseudoElement.toLowerCase() - : pseudoElement.toUpperCase() - - if (pseudoElement === expectedPseudoElement) { return } - - report({ - message: messages.expected(pseudoElement, expectedPseudoElement), - node: rule, - index: pseudoNode.sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-pseudo-element-colon-notation/index.js b/src/rules/selector-pseudo-element-colon-notation/index.js deleted file mode 100644 index a140fe6d4d..0000000000 --- a/src/rules/selector-pseudo-element-colon-notation/index.js +++ /dev/null @@ -1,53 +0,0 @@ -import { - isStandardSyntaxRule, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" -import { levelOneAndTwoPseudoElements } from "../../reference/keywordSets" -import styleSearch from "style-search" - -export const ruleName = "selector-pseudo-element-colon-notation" - -export const messages = ruleMessages(ruleName, { - expected: (q) => `Expected ${q} colon pseudo-element notation`, -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "single", - "double", - ], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const selector = rule.selector - - // get out early if no pseudo elements or classes - if (selector.indexOf(":") === -1) { return } - - // match only level 1 and 2 pseudo elements - const pseudoElementsWithColons = _.toArray(levelOneAndTwoPseudoElements).map(x => `:${x}`) - styleSearch({ source: selector.toLowerCase(), target: pseudoElementsWithColons }, match => { - const prevCharIsColon = selector[match.startIndex - 1] === ":" - - if (expectation === "single" && !prevCharIsColon) { return } - if (expectation === "double" && prevCharIsColon) { return } - - report({ - message: messages.expected(expectation), - node: rule, - index: match.startIndex, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/selector-pseudo-element-no-unknown/index.js b/src/rules/selector-pseudo-element-no-unknown/index.js deleted file mode 100644 index 0de755d323..0000000000 --- a/src/rules/selector-pseudo-element-no-unknown/index.js +++ /dev/null @@ -1,67 +0,0 @@ -import { - isStandardSyntaxRule, - isStandardSyntaxSelector, - optionsMatches, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import { pseudoElements } from "../../reference/keywordSets" -import { vendor } from "postcss" - -export const ruleName = "selector-pseudo-element-no-unknown" - -export const messages = ruleMessages(ruleName, { - rejected: (selector) => `Unexpected unknown pseudo-element selector "${selector}"`, -}) - -export default function (actual, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }, { - actual: options, - possible: { - ignorePseudoElements: [isString], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (!isStandardSyntaxRule(rule)) { return } - const { selector } = rule - - // Return early before parse if no pseudos for performance - if (selector.indexOf(":") === -1) { return } - - parseSelector(selector, result, rule, selectorTree => { - selectorTree.walkPseudos(pseudoNode => { - const { value } = pseudoNode - - if (!isStandardSyntaxSelector(value)) { return } - - // Ignore pseudo-classes - if (value.slice(0, 2) !== "::") { return } - - if (optionsMatches(options, "ignorePseudoElements", pseudoNode.value.slice(2))) { return } - - const name = value.slice(2) - - if ( - vendor.prefix(name) - || pseudoElements.has(name.toLowerCase()) - ) { return } - - report({ - message: messages.rejected(value), - node: rule, - index: pseudoNode.sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-root-no-composition/index.js b/src/rules/selector-root-no-composition/index.js deleted file mode 100644 index 06c5e82d16..0000000000 --- a/src/rules/selector-root-no-composition/index.js +++ /dev/null @@ -1,32 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-root-no-composition" - -export const messages = ruleMessages(ruleName, { - rejected: "Unexpected composition", -}) - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkRules(rule => { - if (rule.selector.toLowerCase().indexOf(":root") === -1 - || rule.selector.toLowerCase().trim() === ":root") { - return - } - - report({ - message: messages.rejected, - node: rule, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/selector-type-case/index.js b/src/rules/selector-type-case/index.js deleted file mode 100644 index 3c81ed878b..0000000000 --- a/src/rules/selector-type-case/index.js +++ /dev/null @@ -1,56 +0,0 @@ -import { - isKeyframeSelector, - isStandardSyntaxRule, - isStandardSyntaxSelector, - isStandardSyntaxTypeSelector, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" - -export const ruleName = "selector-type-case" - -export const messages = ruleMessages(ruleName, { - expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "lower", - "upper", - ], - }) - if (!validOptions) { return } - - root.walkRules(rule => { - const { selector, selectors } = rule - - if (!isStandardSyntaxRule(rule)) { return } - if (!isStandardSyntaxSelector(selector)) { return } - if (selectors.some(s => isKeyframeSelector(s))) { return } - - parseSelector(selector, result, rule, selectorAST => { - selectorAST.walkTags(tag => { - if (!isStandardSyntaxTypeSelector(tag)) { return } - - const { sourceIndex, value } = tag - const expectedValue = expectation === "lower" ? value.toLowerCase() : value.toUpperCase() - - if (value === expectedValue) { return } - - report({ - message: messages.expected(value, expectedValue), - node: rule, - index: sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/selector-type-no-unknown/index.js b/src/rules/selector-type-no-unknown/index.js deleted file mode 100644 index 3d45f450b7..0000000000 --- a/src/rules/selector-type-no-unknown/index.js +++ /dev/null @@ -1,91 +0,0 @@ -import { - isKeyframeSelector, - isStandardSyntaxRule, - isStandardSyntaxSelector, - isStandardSyntaxTypeSelector, - optionsMatches, - parseSelector, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import htmlTags from "html-tags" -import { isString } from "lodash" -import svgTags from "svg-tags" - -export const ruleName = "selector-type-no-unknown" - -export const messages = ruleMessages(ruleName, { - rejected: (selector) => `Unexpected unknown type selector "${selector}"`, -}) - -// htmlTags includes only "standard" tags. So we augment it with older tags etc. -const nonStandardHtmlTags = new Set([ - "acronym", - "applet", - "basefont", - "big", - "blink", - "center", - "content", - "dir", - "font", - "frame", - "frameset", - "hgroup", - "isindex", - "keygen", - "listing", - "marquee", - "noembed", - "plaintext", - "spacer", - "strike", - "tt", - "xmp", -]) - -export default function (actual, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }, { - actual: options, - possible: { - ignoreTypes: [isString], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkRules(rule => { - const { selector, selectors } = rule - - if (!isStandardSyntaxRule(rule)) { return } - if (!isStandardSyntaxSelector(selector)) { return } - if (selectors.some(s => isKeyframeSelector(s))) { return } - - parseSelector(selector, result, rule, selectorTree => { - selectorTree.walkTags(tagNode => { - if (!isStandardSyntaxTypeSelector(tagNode)) { return } - - if (optionsMatches(options, "ignoreTypes", tagNode.value)) { return } - - const tagName = tagNode.value - const tagNameLowerCase = tagName.toLowerCase() - - if (htmlTags.indexOf(tagNameLowerCase) !== -1 - || svgTags.indexOf(tagNameLowerCase) !== -1 - || nonStandardHtmlTags.has(tagNameLowerCase) - ) { return } - - report({ - message: messages.rejected(tagName), - node: rule, - index: tagNode.sourceIndex, - ruleName, - result, - }) - }) - }) - }) - } -} diff --git a/src/rules/shorthand-property-no-redundant-values/index.js b/src/rules/shorthand-property-no-redundant-values/index.js deleted file mode 100644 index b065b68c9b..0000000000 --- a/src/rules/shorthand-property-no-redundant-values/index.js +++ /dev/null @@ -1,119 +0,0 @@ -import { - isStandardSyntaxDeclaration, - isStandardSyntaxProperty, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import shorthandData from "../../reference/shorthandData" -import valueParser from "postcss-value-parser" -import { vendor } from "postcss" - -export const ruleName = "shorthand-property-no-redundant-values" - -export const messages = ruleMessages(ruleName, { - rejected: (unexpected, expected) => `Unexpected longhand value '${unexpected}' instead of '${expected}'`, -}) - -const shorthandableProperties = new Set(Object.keys(shorthandData)) - -const ignoredCharacters = [ - "+", "-", "*", "/", "(", ")", - "$", "@", "--", "var(", -] - -const ignoredShorthandProperties = new Set([ - "background", - "font", - "border", - "border-top", - "border-bottom", - "border-left", - "border-right", - "list-style", - "transition", -]) - -function isIgnoredCharacters(value) { - return ignoredCharacters.some(char => value.indexOf(char) !== -1) -} - -function canCondense(top, right, bottom = null, left = null) { - const lowerTop = top.toLowerCase() - const lowerRight = right.toLowerCase() - const lowerBottom = bottom && bottom.toLowerCase() - const lowerLeft = left && left.toLowerCase() - - if (canCondenseToOneValue(lowerTop, lowerRight, lowerBottom, lowerLeft)) { - return [top] - } else if (canCondenseToTwoValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) { - return [ top, right ] - } else if (canCondenseToThreeValues(lowerTop, lowerRight, lowerBottom, lowerLeft)) { - return [ top, right, bottom ] - } else { - return [ top, right, bottom, left ] - } -} - -function canCondenseToOneValue(top, right, bottom, left) { - if (top !== right) { return false } - - return top === bottom && (bottom === left || !left) || !bottom && !left -} - -function canCondenseToTwoValues(top, right, bottom, left) { - return top === bottom && right === left || top === bottom && !left && top !== right -} - -function canCondenseToThreeValues(top, right, bottom, left) { - return right === left -} - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkDecls(decl => { - if ( - !isStandardSyntaxDeclaration(decl) - || !isStandardSyntaxProperty(decl.prop) - ) { return } - - const { prop, value } = decl - const normalizedProp = vendor.unprefixed(prop.toLowerCase()) - - // Ignore not shorthandable properties, and math operations - if ( - isIgnoredCharacters(value) - || !shorthandableProperties.has(normalizedProp) - || ignoredShorthandProperties.has(normalizedProp) - ) { return } - - const valuesToShorthand = [] - - valueParser(value).walk((valueNode) => { - if (valueNode.type !== "word") { return } - - valuesToShorthand.push(valueParser.stringify(valueNode)) - }) - - if (valuesToShorthand.length <= 1 - || valuesToShorthand.length > 4 - ) { return } - - const shortestForm = canCondense(...valuesToShorthand) - const shortestFormString = shortestForm.filter((value) => { return value }).join(" ") - const valuesFormString = valuesToShorthand.join(" ") - - if (shortestFormString.toLowerCase() === valuesFormString.toLowerCase()) { return } - - report({ - message: messages.rejected(value, shortestFormString), - node: decl, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/string-quotes/index.js b/src/rules/string-quotes/index.js deleted file mode 100644 index ccab90d557..0000000000 --- a/src/rules/string-quotes/index.js +++ /dev/null @@ -1,38 +0,0 @@ -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "string-quotes" - -export const messages = ruleMessages(ruleName, { - expected: q => `Expected ${q} quotes`, -}) - -export default function (expectation) { - const erroneousQuote = (expectation === "single") ? "\"" : "'" - - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "single", - "double", - ], - }) - if (!validOptions) { return } - - const cssString = root.toString() - styleSearch({ source: cssString, target: erroneousQuote }, match => { - report({ - message: messages.expected(expectation), - node: root, - index: match.startIndex, - result, - ruleName, - }) - }) - } -} diff --git a/src/rules/unit-blacklist/index.js b/src/rules/unit-blacklist/index.js deleted file mode 100644 index f596f7f21c..0000000000 --- a/src/rules/unit-blacklist/index.js +++ /dev/null @@ -1,67 +0,0 @@ -import { - atRuleParamIndex, - declarationValueIndex, - getUnitFromValueNode, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" -import validateObjectWithStringArrayProps from "../../utils/validateObjectWithStringArrayProps" -import valueParser from "postcss-value-parser" - -export const ruleName = "unit-blacklist" - -export const messages = ruleMessages(ruleName, { - rejected: (unit) => `Unexpected unit "${unit}"`, -}) - -function rule(blacklistInput, options) { - const blacklist = [].concat(blacklistInput) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: blacklist, - possible: [_.isString], - }, { - optional: true, - actual: options, - possible: { - ignoreProperties: validateObjectWithStringArrayProps, - }, - }) - if (!validOptions) { return } - - function check(node, value, getIndex) { - valueParser(value).walk(function (valueNode) { - // Ignore wrong units within `url` function - if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { return false } - - const unit = getUnitFromValueNode(valueNode) - - if (!unit || (unit && blacklist.indexOf(unit.toLowerCase()) === -1)) { return } - - if (options && optionsMatches(options.ignoreProperties, unit.toLowerCase(), node.prop)) { return } - - report({ - index: getIndex(node) + valueNode.sourceIndex, - message: messages.rejected(unit), - node, - result, - ruleName, - }) - }) - } - - root.walkAtRules(/^media$/i, atRule => - check(atRule, atRule.params, atRuleParamIndex) - ) - root.walkDecls(decl => - check(decl, decl.value, declarationValueIndex) - ) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/unit-case/index.js b/src/rules/unit-case/index.js deleted file mode 100644 index bbcaa12e41..0000000000 --- a/src/rules/unit-case/index.js +++ /dev/null @@ -1,58 +0,0 @@ -import { - atRuleParamIndex, - declarationValueIndex, - getUnitFromValueNode, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import valueParser from "postcss-value-parser" - -export const ruleName = "unit-case" - -export const messages = ruleMessages(ruleName, { - expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, -}) - -export default function (expectation) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "lower", - "upper", - ], - }) - if (!validOptions) { return } - - function check(node, value, getIndex) { - valueParser(value).walk((valueNode) => { - // Ignore wrong units within `url` function - if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { return false } - - const unit = getUnitFromValueNode(valueNode) - - if (!unit) { return } - - const expectedUnit = expectation === "lower" ? unit.toLowerCase() : unit.toUpperCase() - - if (unit === expectedUnit) { return } - - report({ - index: getIndex(node) + valueNode.sourceIndex, - message: messages.expected(unit, expectedUnit), - node, - result, - ruleName, - }) - }) - } - - root.walkAtRules(/^media$/i, atRule => - check(atRule, atRule.params, atRuleParamIndex) - ) - root.walkDecls(decl => - check(decl, decl.value, declarationValueIndex) - ) - } -} diff --git a/src/rules/unit-no-unknown/index.js b/src/rules/unit-no-unknown/index.js deleted file mode 100644 index 1c95193331..0000000000 --- a/src/rules/unit-no-unknown/index.js +++ /dev/null @@ -1,61 +0,0 @@ -import { - atRuleParamIndex, - declarationValueIndex, - getUnitFromValueNode, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import { units } from "../../reference/keywordSets" -import valueParser from "postcss-value-parser" - -export const ruleName = "unit-no-unknown" - -export const messages = ruleMessages(ruleName, { - rejected: (unit) => `Unexpected unknown unit "${unit}"`, -}) - -export default function (actual, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }, { - actual: options, - possible: { - ignoreUnits: [isString], - }, - optional: true, - }) - - if (!validOptions) { return } - - function check(node, value, getIndex) { - valueParser(value).walk(function (valueNode) { - // Ignore wrong units within `url` function - if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { return false } - - const unit = getUnitFromValueNode(valueNode) - if (!unit) { return } - - if (optionsMatches(options, "ignoreUnits", unit)) { return } - - if (units.has(unit.toLowerCase())) { return } - - report({ - index: getIndex(node) + valueNode.sourceIndex, - message: messages.rejected(unit), - node, - result, - ruleName, - }) - }) - } - - root.walkAtRules(/^media$/i, atRule => - check(atRule, atRule.params, atRuleParamIndex) - ) - root.walkDecls(decl => - check(decl, decl.value, declarationValueIndex) - ) - } -} diff --git a/src/rules/unit-whitelist/index.js b/src/rules/unit-whitelist/index.js deleted file mode 100644 index 9c53faaa1b..0000000000 --- a/src/rules/unit-whitelist/index.js +++ /dev/null @@ -1,67 +0,0 @@ -import { - atRuleParamIndex, - declarationValueIndex, - getUnitFromValueNode, - optionsMatches, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import _ from "lodash" -import validateObjectWithStringArrayProps from "../../utils/validateObjectWithStringArrayProps" -import valueParser from "postcss-value-parser" - -export const ruleName = "unit-whitelist" - -export const messages = ruleMessages(ruleName, { - rejected: (unit) => `Unexpected unit "${unit}"`, -}) - -function rule(whitelistInput, options) { - const whitelist = [].concat(whitelistInput) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: whitelist, - possible: [_.isString], - }, { - optional: true, - actual: options, - possible: { - ignoreProperties: validateObjectWithStringArrayProps, - }, - }) - if (!validOptions) { return } - - function check(node, value, getIndex) { - valueParser(value).walk(function (valueNode) { - // Ignore wrong units within `url` function - if (valueNode.type === "function" && valueNode.value.toLowerCase() === "url") { return false } - - const unit = getUnitFromValueNode(valueNode) - - if (!unit || (unit && whitelist.indexOf(unit.toLowerCase()) !== -1)) { return } - - if (options && optionsMatches(options["ignoreProperties"], unit.toLowerCase(), node.prop)) { return } - - report({ - index: getIndex(node) + valueNode.sourceIndex, - message: messages.rejected(unit), - node, - result, - ruleName, - }) - }) - } - - root.walkAtRules(/^media$/i, atRule => - check(atRule, atRule.params, atRuleParamIndex) - ) - root.walkDecls(decl => - check(decl, decl.value, declarationValueIndex) - ) - } -} - -rule.primaryOptionArray = true - -export default rule diff --git a/src/rules/value-keyword-case/index.js b/src/rules/value-keyword-case/index.js deleted file mode 100644 index 76f73b05d2..0000000000 --- a/src/rules/value-keyword-case/index.js +++ /dev/null @@ -1,138 +0,0 @@ -import { - animationNameKeywords, - animationShorthandKeywords, - camelCaseKeywords, - fontFamilyKeywords, - fontShorthandKeywords, - gridAreaKeywords, - gridColumnKeywords, - gridRowKeywords, - listStyleShorthandKeywords, - listStyleTypeKeywords, - systemColors, -} from "../../reference/keywordSets" -import { - declarationValueIndex, - getUnitFromValueNode, - isCounterIncrementCustomIdentValue, - isStandardSyntaxValue, - matchesStringOrRegExp, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import { isString } from "lodash" -import valueParser from "postcss-value-parser" - -export const ruleName = "value-keyword-case" - -export const messages = ruleMessages(ruleName, { - expected: (actual, expected) => `Expected "${actual}" to be "${expected}"`, -}) - -// Operators are interpreted as "words" by the value parser, so we want to make sure to ignore them. -const ignoredCharacters = new Set([ - "+", "-", "/", "*", "%", -]) - -const mapLowercaseKeywordsToCamelCase = new Map() -camelCaseKeywords.forEach(func => { - mapLowercaseKeywordsToCamelCase.set(func.toLowerCase(), func) -}) - -export default function (expectation, options) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "lower", - "upper", - ], - }, { - actual: options, - possible: { - ignoreKeywords: [isString], - }, - optional: true, - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const { prop, value } = decl - - valueParser(value).walk((node) => { - const valueLowerCase = node.value.toLowerCase() - - // Ignore system colors - if (systemColors.has(valueLowerCase)) { return } - - // Ignore keywords within `url` and `var` function - if ( - node.type === "function" && ( - valueLowerCase === "url" - || valueLowerCase === "var" - ) - ) { return false } - - const keyword = node.value - - // Ignore css variables, and hex values, and math operators, and sass interpolation - if (node.type !== "word" - || !isStandardSyntaxValue(node.value) - || value.indexOf("#") !== -1 - || ignoredCharacters.has(keyword) - || getUnitFromValueNode(node) - ) { return } - - if (prop === "animation" - && !animationShorthandKeywords.has(valueLowerCase) - && !animationNameKeywords.has(valueLowerCase) - ) { return } - if (prop === "animation-name" && !animationNameKeywords.has(valueLowerCase)) { return } - if (prop === "font" - && !fontShorthandKeywords.has(valueLowerCase) - && !fontFamilyKeywords.has(valueLowerCase) - ) { - return - } - if (prop === "font-family" && !fontFamilyKeywords.has(valueLowerCase)) { return } - if (prop === "counter-increment" && isCounterIncrementCustomIdentValue(valueLowerCase)) { return } - if (prop === "grid-row" && !gridRowKeywords.has(valueLowerCase)) { return } - if (prop === "grid-column" && !gridColumnKeywords.has(valueLowerCase)) { return } - if (prop === "grid-area" && !gridAreaKeywords.has(valueLowerCase)) { return } - if (prop === "list-style" - && !listStyleShorthandKeywords.has(valueLowerCase) - && !listStyleTypeKeywords.has(valueLowerCase) - ) { return } - if (prop === "list-style-type" && !listStyleTypeKeywords.has(valueLowerCase)) { return } - - const ignoreKeywords = options && options.ignoreKeywords || [] - - if (ignoreKeywords.length > 0 && matchesStringOrRegExp(keyword, ignoreKeywords)) { return } - - const keywordLowerCase = keyword.toLocaleLowerCase() - let expectedKeyword = null - - if (expectation === "lower" - && mapLowercaseKeywordsToCamelCase.has(keywordLowerCase) - ) { - expectedKeyword = mapLowercaseKeywordsToCamelCase.get(keywordLowerCase) - } else if (expectation === "lower") { - expectedKeyword = keyword.toLowerCase() - } else { - expectedKeyword = keyword.toUpperCase() - } - - if (keyword === expectedKeyword) { return } - - report({ - message: messages.expected(keyword, expectedKeyword), - node: decl, - index: declarationValueIndex(decl) + node.sourceIndex, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/rules/value-list-comma-newline-after/index.js b/src/rules/value-list-comma-newline-after/index.js deleted file mode 100644 index 39109bc936..0000000000 --- a/src/rules/value-list-comma-newline-after/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { valueListCommaWhitespaceChecker } from "../value-list-comma-space-after" - -export const ruleName = "value-list-comma-newline-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected newline after \",\"", - expectedAfterMultiLine: () => "Expected newline after \",\" in a multi-line list", - rejectedAfterMultiLine: () => "Unexpected whitespace after \",\" in a multi-line list", -}) - -export default function (expectation) { - const checker = whitespaceChecker("newline", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], - }) - if (!validOptions) { return } - - valueListCommaWhitespaceChecker({ - root, - result, - locationChecker: checker.afterOneOnly, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/value-list-comma-newline-before/index.js b/src/rules/value-list-comma-newline-before/index.js deleted file mode 100644 index 17aaf42a46..0000000000 --- a/src/rules/value-list-comma-newline-before/index.js +++ /dev/null @@ -1,36 +0,0 @@ -import { - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import { valueListCommaWhitespaceChecker } from "../value-list-comma-space-after" - -export const ruleName = "value-list-comma-newline-before" - -export const messages = ruleMessages(ruleName, { - expectedBefore: () => "Expected newline before \",\"", - expectedBeforeMultiLine: () => "Expected newline before \",\" in a multi-line list", - rejectedBeforeMultiLine: () => "Unexpected whitespace before \",\" in a multi-line list", -}) - -export default function (expectation) { - const checker = whitespaceChecker("newline", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "always-multi-line", - "never-multi-line", - ], - }) - if (!validOptions) { return } - - valueListCommaWhitespaceChecker({ - root, - result, - locationChecker: checker.beforeAllowingIndentation, - checkedRuleName: ruleName, - }) - } -} diff --git a/src/rules/value-list-comma-space-after/index.js b/src/rules/value-list-comma-space-after/index.js deleted file mode 100644 index 5a4aa9d511..0000000000 --- a/src/rules/value-list-comma-space-after/index.js +++ /dev/null @@ -1,73 +0,0 @@ -import { - isStandardSyntaxDeclaration, - isStandardSyntaxProperty, - report, - ruleMessages, - validateOptions, - whitespaceChecker, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "value-list-comma-space-after" - -export const messages = ruleMessages(ruleName, { - expectedAfter: () => "Expected single space after \",\"", - rejectedAfter: () => "Unexpected whitespace after \",\"", - expectedAfterSingleLine: () => "Expected single space after \",\" in a single-line list", - rejectedAfterSingleLine: () => "Unexpected whitespace after \",\" in a single-line list", -}) - -export default function (expectation) { - const checker = whitespaceChecker("space", expectation, messages) - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: expectation, - possible: [ - "always", - "never", - "always-single-line", - "never-single-line", - ], - }) - if (!validOptions) { return } - - valueListCommaWhitespaceChecker({ - root, - result, - locationChecker: checker.after, - checkedRuleName: ruleName, - }) - } -} - -export function valueListCommaWhitespaceChecker({ locationChecker, root, result, checkedRuleName }) { - root.walkDecls(decl => { - if ( - !isStandardSyntaxDeclaration(decl) - || !isStandardSyntaxProperty(decl.prop) - ) { return } - styleSearch({ - source: decl.toString(), - target: ",", - functionArguments: "skip", - }, match => { - checkComma(decl.toString(), match.startIndex, decl) - }) - }) - - function checkComma(source, index, node) { - locationChecker({ - source, - index, - err: m => { - report({ - message: m, - node, - index, - result, - ruleName: checkedRuleName, - }) - }, - }) - } -} diff --git a/src/rules/value-list-max-empty-lines/index.js b/src/rules/value-list-max-empty-lines/index.js deleted file mode 100644 index 2476c37fa2..0000000000 --- a/src/rules/value-list-max-empty-lines/index.js +++ /dev/null @@ -1,53 +0,0 @@ -import { - isNumber, - repeat, -} from "lodash" -import { - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "value-list-max-empty-lines" - -export const messages = ruleMessages(ruleName, { - expected: max => `Expected no more than ${max} empty line(s)`, -}) - -export default function (max) { - const maxAdjacentNewlines = max + 1 - - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { - actual: max, - possible: isNumber, - }) - if (!validOptions) { return } - - root.walkDecls(decl => { - const value = decl.value - const repeatLFNewLines = repeat("\n", maxAdjacentNewlines) - const repeatCRLFNewLines = repeat("\r\n", maxAdjacentNewlines) - - styleSearch({ source: value, target: "\n" }, match => { - if ( - value.substr(match.startIndex + 1, maxAdjacentNewlines) === repeatLFNewLines - || value.substr(match.startIndex + 1, maxAdjacentNewlines * 2) === repeatCRLFNewLines - ) { - // Put index at `\r` if it's CRLF, otherwise leave it at `\n` - let index = match.startIndex - if (value[index - 1] === "\r") { index -= 1 } - - report({ - message: messages.expected(max), - node: decl, - index, - result, - ruleName, - }) - } - }) - }) - } -} diff --git a/src/rules/value-no-vendor-prefix/index.js b/src/rules/value-no-vendor-prefix/index.js deleted file mode 100644 index 18be507db2..0000000000 --- a/src/rules/value-no-vendor-prefix/index.js +++ /dev/null @@ -1,48 +0,0 @@ -import { - isAutoprefixable, - isStandardSyntaxDeclaration, - isStandardSyntaxProperty, - report, - ruleMessages, - validateOptions, -} from "../../utils" -import styleSearch from "style-search" - -export const ruleName = "value-no-vendor-prefix" - -export const messages = ruleMessages(ruleName, { - rejected: value => `Unexpected vendor-prefix "${value}"`, -}) - -const valuePrefixes = [ "-webkit-", "-moz-", "-ms-", "-o-" ] - -export default function (actual) { - return (root, result) => { - const validOptions = validateOptions(result, ruleName, { actual }) - if (!validOptions) { return } - - root.walkDecls(decl => { - if ( - !isStandardSyntaxDeclaration(decl) - || !isStandardSyntaxProperty(decl.prop) - || decl.value[0] !== "-" - ) { return } - - const { prop, value } = decl - - // Search the full declaration in order to get an accurate index - styleSearch({ source: value.toLowerCase(), target: valuePrefixes }, match => { - const fullIdentifier = /^(-[a-z-]+)\b/i.exec(value.slice(match.startIndex))[1] - if (!isAutoprefixable.propertyValue(prop, fullIdentifier)) { return } - - report({ - message: messages.rejected(fullIdentifier), - node: decl, - index: prop.length + decl.raws.between.length + match.startIndex, - result, - ruleName, - }) - }) - }) - } -} diff --git a/src/standalone.js b/src/standalone.js deleted file mode 100644 index 794a3100c0..0000000000 --- a/src/standalone.js +++ /dev/null @@ -1,138 +0,0 @@ -/* @flow */ -import * as formatters from "./formatters" -import createStylelint from "./createStylelint" -import globby from "globby" -import needlessDisables from "./needlessDisables" - -const alwaysIgnoredGlobs = [ - "!**/node_modules/**", - "!**/bower_components/**", -] - -export default function ({ - files, - code, - codeFilename, - config, - configFile, - configBasedir, - configOverrides, - ignoreDisables, - ignorePath, - reportNeedlessDisables, - formatter, - syntax, - customSyntax, - allowEmptyInput, -}: stylelint$standaloneOptions = {}): Promise { - const isValidCode = typeof code === "string" - if (!files && !isValidCode || files && (code || isValidCode)) { - throw new Error("You must pass stylelint a `files` glob or a `code` string, though not both") - } - - let formatterFunction: Function - if (typeof formatter === "string") { - formatterFunction = formatters[formatter] - if (formatterFunction === undefined) { - return Promise.reject(new Error("You must use a valid formatter option: 'json', 'string', 'verbose', or a function")) - } - } else if (typeof formatter === "function") { - formatterFunction = formatter - } else { - formatterFunction = formatters.json - } - - const stylelint = createStylelint({ - config, - configFile, - configBasedir, - configOverrides, - ignoreDisables, - ignorePath, - reportNeedlessDisables, - syntax, - customSyntax, - }) - - if (!files) { - return stylelint._lintSource({ code, codeFilename }) - .then((postcssResult) => { - return stylelint._createStylelintResult(postcssResult) - }) - .catch(handleError) - .then((stylelintResult) => { - return prepareReturnValue([stylelintResult]) - }) - } - - return globby([].concat(files, alwaysIgnoredGlobs)) - .then((filePaths) => { - if (!filePaths.length) { - if (allowEmptyInput === undefined || !allowEmptyInput) { - const err: Object = new Error("Files glob patterns specified did not match any files") - err.code = 80 - throw err - } else { - return Promise.all([]) - } - } - - const getStylelintResults = filePaths.map((filePath) => { - return stylelint._lintSource({ filePath }) - .then((postcssResult) => { - return stylelint._createStylelintResult(postcssResult, filePath) - }) - .catch(handleError) - }) - - return Promise.all(getStylelintResults) - }) - .then(prepareReturnValue) - - function prepareReturnValue( - stylelintResults: Array, - ): stylelint$standaloneReturnValue { - const errored = stylelintResults.some((result) => result.errored) - const returnValue: stylelint$standaloneReturnValue = { - errored, - output: formatterFunction(stylelintResults), - results: stylelintResults, - } - if (reportNeedlessDisables) { - returnValue.needlessDisables = needlessDisables(stylelintResults) - } - return returnValue - } -} - -function handleError(error) { - if (error.name === "CssSyntaxError") { - return convertCssSyntaxErrorToResult(error) - } else { - throw error - } -} - -// By converting syntax errors to stylelint results, -// we can control their appearance in the formatted output -// and other tools like editor plugins can decide how to -// present them, as well -function convertCssSyntaxErrorToResult( - error: Object, -): stylelint$result { - if (error.name !== "CssSyntaxError") { throw error } - - return { - source: error.file || "", - deprecations: [], - invalidOptionWarnings: [], - errored: true, - warnings: [{ - line: error.line, - column: error.column, - rule: error.name, - severity: "error", - text: error.reason + " (" + error.name + ")", - }], - } -} diff --git a/src/testUtils/basicChecks.js b/src/testUtils/basicChecks.js deleted file mode 100644 index e5dc21ebbe..0000000000 --- a/src/testUtils/basicChecks.js +++ /dev/null @@ -1,19 +0,0 @@ -// These should pass for *almost* every rule -export default [ - { - code: "", - description: "empty stylesheet", - }, - { - code: "a {}", - description: "empty rule", - }, - { - code: "@import \"foo.css\";", - description: "blockless statement", - }, - { - code: ":global {}", - description: "CSS Modules global empty rule set", - }, -] diff --git a/src/testUtils/index.js b/src/testUtils/index.js deleted file mode 100644 index a7e1d3b602..0000000000 --- a/src/testUtils/index.js +++ /dev/null @@ -1,2 +0,0 @@ -export { default as mergeTestDescriptions } from "./mergeTestDescriptions" -export { default as testRule } from "./testRule" diff --git a/src/testUtils/mergeTestDescriptions.js b/src/testUtils/mergeTestDescriptions.js deleted file mode 100644 index 00fc3404c8..0000000000 --- a/src/testUtils/mergeTestDescriptions.js +++ /dev/null @@ -1,11 +0,0 @@ -import _ from "lodash" - -export default function (...objs) { - return _.mergeWith({}, ...objs, mergeCustomizer) -} - -function mergeCustomizer(objValue, srcValue) { - if (_.isArray(objValue, mergeCustomizer)) { - return objValue.concat(srcValue) - } -} diff --git a/src/testUtils/testRule.js b/src/testUtils/testRule.js deleted file mode 100644 index dab7051668..0000000000 --- a/src/testUtils/testRule.js +++ /dev/null @@ -1,18 +0,0 @@ -import createRuleTester from "./createRuleTester" -import test from "tape" - -// This code is included here instead of using stylelint-test-rule-tape -// because tests performs significantly faster this way -function assertEquality(processCss, context) { - const testFn = (context.only) ? test.only : test - testFn(context.caseDescription, t => { - t.plan(context.comparisonCount) - processCss.then((comparisons) => { - comparisons.forEach(({ actual, expected, description }) => { - t.equal(actual, expected, description) - }) - }) - }) -} - -export default createRuleTester(assertEquality) diff --git a/src/utils/__tests__/containsString-test.js b/src/utils/__tests__/containsString-test.js deleted file mode 100644 index 7658ed76a8..0000000000 --- a/src/utils/__tests__/containsString-test.js +++ /dev/null @@ -1,35 +0,0 @@ -import containsString from "../containsString" -import test from "tape" - -test("containsString comparing with string comparison values", t => { - t.deepEqual(containsString("bar", "bar"), - { match: "bar", pattern: "bar" }) - t.deepEqual(containsString("foo bar something", "bar"), - { match: "foo bar something", pattern: "bar" }) - t.notOk(containsString("bar", "foo")) - t.deepEqual(containsString("/bar something", "/bar"), - { match: "/bar something", pattern: "/bar" }) - t.deepEqual(containsString("bar something/", "something/"), - { match: "bar something/", pattern: "something/" }) - t.notOk(containsString("/bar/", "/bar/")) - t.notOk(containsString("/bar/ something", "/bar/")) - t.notOk(containsString("bar", "")) - t.notOk(containsString("bar", null)) - - t.end() -}) - -test("containsString comparing with array comparison values", t => { - t.deepEqual(containsString("bar", [ "foo", "bar" ]), - { match: "bar", pattern: "bar" }) - t.deepEqual(containsString("foo baz something", [ "bar", "baz" ]), - { match: "foo baz something", pattern: "baz" }) - t.deepEqual(containsString("foo bar", [ "bar", "foo" ]), - { match: "foo bar", pattern: "bar" }) - t.notOk(containsString("bar", [ "foo", "baz" ])) - t.notOk(containsString("/bar/", ["/bar/"])) - t.notOk(containsString("/bar/ something", [ "/bar/", "foo" ])) - t.notOk(containsString("bar", [])) - - t.end() -}) diff --git a/src/utils/__tests__/findAnimationName-test.js b/src/utils/__tests__/findAnimationName-test.js deleted file mode 100644 index 21f3bf6546..0000000000 --- a/src/utils/__tests__/findAnimationName-test.js +++ /dev/null @@ -1,111 +0,0 @@ -import findAnimationName from "../findAnimationName" -import test from "tape" - -test("findAnimationName", t => { - t.deepEqual( - findAnimationName("inherit"), - [{ - sourceIndex: 0, - type: "word", - value: "inherit", - }] - ) - t.deepEqual( - findAnimationName("INHERIT"), - [{ - sourceIndex: 0, - type: "word", - value: "INHERIT", - }] - ) - t.deepEqual( - findAnimationName("3s @varialbe"), - [] - ) - t.deepEqual( - findAnimationName("3s #{$variable}"), - [] - ) - t.deepEqual( - findAnimationName("none"), - [] - ) - t.deepEqual( - findAnimationName("slidein"), - [{ - sourceIndex: 0, - type: "word", - value: "slidein", - }] - ) - t.deepEqual( - findAnimationName("3s slidein"), - [{ - sourceIndex: 3, - type: "word", - value: "slidein", - }] - ) - t.deepEqual( - findAnimationName("none slidein"), - [{ - sourceIndex: 5, - type: "word", - value: "slidein", - }] - ) - t.deepEqual( - findAnimationName("3s linear 1s slidein"), - [{ - sourceIndex: 13, - type: "word", - value: "slidein", - }] - ) - t.deepEqual( - findAnimationName("3S LINEAR 1S SLIDEIN"), - [{ - sourceIndex: 13, - type: "word", - value: "SLIDEIN", - }] - ) - t.deepEqual( - findAnimationName("3s ease-in 1s 2 reverse both paused slidein"), - [{ - sourceIndex: 36, - type: "word", - value: "slidein", - }] - ) - t.deepEqual( - findAnimationName("go-left-right 3s infinite alternate"), - [{ - sourceIndex: 0, - type: "word", - value: "go-left-right", - }] - ) - t.deepEqual( - findAnimationName("shrink 2s ease-out, pulsate 4s 2s infinite ease-in-out"), - [ { - sourceIndex: 0, - type: "word", - value: "shrink", - }, { - sourceIndex: 20, - type: "word", - value: "pulsate", - } ] - ) - t.deepEqual( - findAnimationName("drive 4s steps(4, end) infinite"), - [{ - sourceIndex: 0, - type: "word", - value: "drive", - }] - ) - - t.end() -}) diff --git a/src/utils/__tests__/findFontFamily-test.js b/src/utils/__tests__/findFontFamily-test.js deleted file mode 100644 index e716a2457c..0000000000 --- a/src/utils/__tests__/findFontFamily-test.js +++ /dev/null @@ -1,331 +0,0 @@ -import findFontFamily from "../findFontFamily" -import test from "tape" - -test("findFontFamily", t => { - t.deepEqual( - findFontFamily("inherit"), - [{ - sourceIndex: 0, - type: "word", - value: "inherit", - }] - ) - t.deepEqual( - findFontFamily("INHERIT"), - [{ - sourceIndex: 0, - type: "word", - value: "INHERIT", - }] - ) - t.deepEqual( - findFontFamily("12pt/10pt sans-serif"), - [{ - sourceIndex: 10, - type: "word", - value: "sans-serif", - }] - ) - t.deepEqual( - findFontFamily("12pt/10pt Red/Black"), - [{ - sourceIndex: 10, - type: "word", - value: "Red/Black", - }] - ) - t.deepEqual( - findFontFamily("12pt/10pt Red/Black/Three"), - [{ - sourceIndex: 10, - type: "word", - value: "Red/Black/Three", - }] - ) - t.deepEqual( - findFontFamily("12pt/10pt Hawaii 5-0"), - [{ - sourceIndex: 10, - type: "word", - value: "Hawaii 5-0", - }] - ) - t.deepEqual( - findFontFamily("12pt/10pt Hawaii 5-0 Font"), - [{ - sourceIndex: 10, - type: "word", - value: "Hawaii 5-0 Font", - }] - ) - t.deepEqual( - findFontFamily("12pt/10pt /* serif */ Hawaii 5-0"), - [{ - sourceIndex: 22, - type: "word", - value: "Hawaii 5-0", - }] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px $font, serif"), - [{ - sourceIndex: 29, - type: "word", - value: "serif", - }] - ) - t.deepEqual( - findFontFamily("ITALIC BOLD 12PX/30PX serif"), - [{ - sourceIndex: 22, - type: "word", - value: "serif", - }] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px #{$font}, serif"), - [{ - sourceIndex: 32, - type: "word", - value: "serif", - }] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px @font, serif"), - [{ - sourceIndex: 29, - type: "word", - value: "serif", - }] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px var(--font), serif"), - [{ - sourceIndex: 35, - type: "word", - value: "serif", - }] - ) - t.deepEqual( - findFontFamily("bold italic 110% serif"), - [{ - sourceIndex: 17, - type: "word", - value: "serif", - }] - ) - t.deepEqual( - findFontFamily("normal small-caps 12px/14px fantasy"), - [{ - sourceIndex: 28, - type: "word", - value: "fantasy", - }] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px Georgia, serif"), - [ - { - sourceIndex: 22, - type: "word", - value: "Georgia", - }, - { - sourceIndex: 31, - type: "word", - value: "serif", - }, - ] - ) - t.deepEqual( - findFontFamily("italic bold 12px / 30px Georgia, serif"), - [ - { - sourceIndex: 29, - type: "word", - value: "Georgia", - }, - { - sourceIndex: 39, - type: "word", - value: "serif", - }, - ] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px Lucida Grande, Arial, sans-serif"), - [ - { - sourceIndex: 22, - type: "word", - value: "Lucida Grande", - }, - { - sourceIndex: 37, - type: "word", - value: "Arial", - }, - { - sourceIndex: 44, - type: "word", - value: "sans-serif", - }, - ] - ) - t.deepEqual( - findFontFamily("italic bold 12px/ 30px Lucida Grande, Arial, sans-serif"), - [ - { - sourceIndex: 23, - type: "word", - value: "Lucida Grande", - }, - { - sourceIndex: 38, - type: "word", - value: "Arial", - }, - { - sourceIndex: 45, - type: "word", - value: "sans-serif", - }, - ] - ) - t.deepEqual( - findFontFamily("italic bold 12px /30px Lucida Grande, Arial, sans-serif"), - [ - { - sourceIndex: 23, - type: "word", - value: "Lucida Grande", - }, - { - sourceIndex: 38, - type: "word", - value: "Arial", - }, - { - sourceIndex: 45, - type: "word", - value: "sans-serif", - }, - ] - ) - t.deepEqual( - findFontFamily("italic 1000 12px /30px Lucida Grande, Arial, sans-serif"), - [ - { - sourceIndex: 23, - type: "word", - value: "Lucida Grande", - }, - { - sourceIndex: 38, - type: "word", - value: "Arial", - }, - { - sourceIndex: 45, - type: "word", - value: "sans-serif", - }, - ] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px Lucida Grande, Arial, sans-serif"), - [ - { - sourceIndex: 22, - type: "word", - value: "Lucida Grande", - }, - { - sourceIndex: 37, - type: "word", - value: "Arial", - }, - { - sourceIndex: 44, - type: "word", - value: "sans-serif", - }, - ] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px \"Red/Black\", Arial, sans-serif"), - [ - { - quote: "\"", - sourceIndex: 22, - type: "string", - value: "Red/Black", - }, - { - sourceIndex: 35, - type: "word", - value: "Arial", - }, - { - sourceIndex: 42, - type: "word", - value: "sans-serif", - }, - ] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px Arial, \"Ahem!\", sans-serif"), - [ - { - sourceIndex: 22, - type: "word", - value: "Arial", - }, - { - quote: "\"", - sourceIndex: 29, - type: "string", - value: "Ahem!", - }, - { - sourceIndex: 38, - type: "word", - value: "sans-serif", - }, - ] - ) - t.deepEqual( - findFontFamily("italic bold 12px/30px \"Hawaii 5-0\", Arial, sans-serif"), - [ - { - quote: "\"", - sourceIndex: 22, - type: "string", - value: "Hawaii 5-0", - }, - { - sourceIndex: 36, - type: "word", - value: "Arial", - }, - { - sourceIndex: 43, - type: "word", - value: "sans-serif", - }, - ] - ) - - t.deepEqual( - findFontFamily("16px/3 Arial"), - [ - { - sourceIndex: 7, - type: "word", - value: "Arial", - }, - ] - ) - - t.end() -}) diff --git a/src/utils/__tests__/findListStyleType-test.js b/src/utils/__tests__/findListStyleType-test.js deleted file mode 100644 index 0fa33053c4..0000000000 --- a/src/utils/__tests__/findListStyleType-test.js +++ /dev/null @@ -1,155 +0,0 @@ -import findListStyleType from "../findListStyleType" -import test from "tape" - -test("findListStyleType", t => { - t.deepEqual( - findListStyleType("inherit"), - [{ - sourceIndex: 0, - type: "word", - value: "inherit", - }] - ) - t.deepEqual( - findListStyleType("INHERIT"), - [{ - sourceIndex: 0, - type: "word", - value: "INHERIT", - }] - ) - t.deepEqual( - findListStyleType("none"), - [{ - sourceIndex: 0, - type: "word", - value: "none", - }] - ) - t.deepEqual( - findListStyleType("circle"), - [{ - sourceIndex: 0, - type: "word", - value: "circle", - }] - ) - t.deepEqual( - findListStyleType("inside"), - [] - ) - t.deepEqual( - findListStyleType("url('kayo.jpg')"), - [] - ) - t.deepEqual( - findListStyleType("url('marker.gif') inside"), - [] - ) - t.deepEqual( - findListStyleType("square outside"), - [{ - sourceIndex: 0, - type: "word", - value: "square", - }] - ) - t.deepEqual( - findListStyleType("inside square"), - [{ - sourceIndex: 7, - type: "word", - value: "square", - }] - ) - t.deepEqual( - findListStyleType("square inside url('sqpurple.gif')"), - [{ - sourceIndex: 0, - type: "word", - value: "square", - }] - ) - t.deepEqual( - findListStyleType("SQUARE INSIDE URL('sqpurple.gif')"), - [{ - sourceIndex: 0, - type: "word", - value: "SQUARE", - }] - ) - t.deepEqual( - findListStyleType("url('sqpurple.gif') square inside"), - [{ - sourceIndex: 20, - type: "word", - value: "square", - }] - ) - t.deepEqual( - findListStyleType("square url('sqpurple.gif') inside"), - [{ - sourceIndex: 0, - type: "word", - value: "square", - }] - ) - t.deepEqual( - findListStyleType("custom-counter-style"), - [{ - sourceIndex: 0, - type: "word", - value: "custom-counter-style", - }] - ) - t.deepEqual( - findListStyleType("customCounterStyle"), - [{ - sourceIndex: 0, - type: "word", - value: "customCounterStyle", - }] - ) - t.deepEqual( - findListStyleType("inside custom-counter-style"), - [{ - sourceIndex: 7, - type: "word", - value: "custom-counter-style", - }] - ) - t.deepEqual( - findListStyleType("custom-counter-style outside"), - [{ - sourceIndex: 0, - type: "word", - value: "custom-counter-style", - }] - ) - t.deepEqual( - findListStyleType("url('sqpurple.gif') custom-counter-style outside"), - [{ - sourceIndex: 20, - type: "word", - value: "custom-counter-style", - }] - ) - t.deepEqual( - findListStyleType("custom-counter-style url('sqpurple.gif') outside"), - [{ - sourceIndex: 0, - type: "word", - value: "custom-counter-style", - }] - ) - t.deepEqual( - findListStyleType("url('sqpurple.gif') outside custom-counter-style"), - [{ - sourceIndex: 28, - type: "word", - value: "custom-counter-style", - }] - ) - - t.end() -}) diff --git a/src/utils/__tests__/nodeContextLookup-test.js b/src/utils/__tests__/nodeContextLookup-test.js deleted file mode 100644 index eb10a5db3f..0000000000 --- a/src/utils/__tests__/nodeContextLookup-test.js +++ /dev/null @@ -1,56 +0,0 @@ -import nodeContextLookup from "../nodeContextLookup" -import path from "path" -import postcss from "postcss" -import postcssImport from "postcss-import" -import test from "tape" - -test("nodeContextLookup checking media context", t => { - const testLookup = nodeContextLookup() - - t.plan(8) - postcss([postcssImport()]) - .process("@import 'fixtures/one.css'; @import 'fixtures/two.css';", { - from: path.join(__dirname, "fake.css"), - }) - .then(result => { - const rulesBySelector = {} - result.root.walkRules(rule => { - rulesBySelector[rule.selector] = rule - }) - - // a-d are in one file; e-h in another - t.equal( - testLookup.getContext(rulesBySelector.a, rulesBySelector.a.parent), - testLookup.getContext(rulesBySelector.b, rulesBySelector.b.parent) - ) - t.notEqual( - testLookup.getContext(rulesBySelector.a, rulesBySelector.a.parent), - testLookup.getContext(rulesBySelector.c, rulesBySelector.c.parent) - ) - t.notEqual( - testLookup.getContext(rulesBySelector.a, rulesBySelector.a.parent), - testLookup.getContext(rulesBySelector.e, rulesBySelector.e.parent) - ) - t.equal( - testLookup.getContext(rulesBySelector.c, rulesBySelector.c.parent), - testLookup.getContext(rulesBySelector.d, rulesBySelector.d.parent) - ) - t.notEqual( - testLookup.getContext(rulesBySelector.c, rulesBySelector.c.parent), - testLookup.getContext(rulesBySelector.g, rulesBySelector.g.parent) - ) - t.equal( - testLookup.getContext(rulesBySelector.e, rulesBySelector.e.parent), - testLookup.getContext(rulesBySelector.f, rulesBySelector.f.parent) - ) - t.notEqual( - testLookup.getContext(rulesBySelector.f, rulesBySelector.f.parent), - testLookup.getContext(rulesBySelector.g, rulesBySelector.g.parent) - ) - t.equal( - testLookup.getContext(rulesBySelector.g, rulesBySelector.g.parent), - testLookup.getContext(rulesBySelector.h, rulesBySelector.h.parent) - ) - }) - .catch(err => console.log(err.stack)) // eslint-disable-line -}) diff --git a/src/utils/beforeBlockString.js b/src/utils/beforeBlockString.js deleted file mode 100644 index 89210e79ba..0000000000 --- a/src/utils/beforeBlockString.js +++ /dev/null @@ -1,36 +0,0 @@ -/* @flow */ -/** - * Given a CSS statement, return the string before the block. - * For rules, this is the selector list (and surrounding whitespace). - * For at-rules, this is the name and params (and surrounding whitespace). - * - * If there is no block, return empty string. - * - * @param {Rule|AtRule} statement - postcss rule or at-rule node - * @param {object} options - * @param {boolean} [options.noRawBefore] - Leave out the `before` string - * @return {string} - */ -export default function ( - statement: Object, - { noRawBefore }: { noRawBefore: ?boolean} = {} -): string { - let result = "" - let rule: postcss$rule - let atRule: postcss$atRule - - if (statement.type === "rule") { rule = statement } - if (statement.type === "atrule") { atRule = statement } - - if (!rule && !atRule) { return result } - - const before = statement.raws.before - const between = statement.raws.between - - if (!noRawBefore) { result += before } - if (rule) { result += rule.selector } - if (atRule) { result += "@" + atRule.name + atRule.raws.afterName + atRule.params } - if (between !== undefined) { result += between } - - return result -} diff --git a/src/utils/blurComments.js b/src/utils/blurComments.js deleted file mode 100644 index 458c8a70c4..0000000000 --- a/src/utils/blurComments.js +++ /dev/null @@ -1,14 +0,0 @@ -/* @flow */ -/** - * Replace all comments with some innocuous character. - * - * @param {string} source - * @param {[string]} blurChar="`" - * @return {string} - The result string, with comments replaced - */ -export default function ( - source: string, - blurChar: string = "`" -): string { - return source.replace(/\/\*.*\*\//g, blurChar) -} diff --git a/src/utils/blurInterpolation.js b/src/utils/blurInterpolation.js deleted file mode 100644 index 5a818a81d1..0000000000 --- a/src/utils/blurInterpolation.js +++ /dev/null @@ -1,16 +0,0 @@ -/* @flow */ -/** - * Replace all of the characters that are interpolation with some innocuous character. - * - * For example: - * blurFunctionArguments("abc url(abc) abc", "url") === "abc url(```) abc" - * - * @param {string} source - * @return {string} - The result string, with the interpolation characters "blurred" - */ -export default function ( - source: string, - blurChar: string = " " -): string { - return source.replace(/[#@{}]+/g, blurChar) -} diff --git a/src/utils/findAnimationName.js b/src/utils/findAnimationName.js deleted file mode 100644 index e9ca41af2e..0000000000 --- a/src/utils/findAnimationName.js +++ /dev/null @@ -1,48 +0,0 @@ -import { - animationShorthandKeywords, - basicKeywords, -} from "../reference/keywordSets" -import { - getUnitFromValueNode, - isStandardSyntaxValue, - isVariable, -} from "./" -import postcssValueParser from "postcss-value-parser" - -/** - * Get the font-families within a `font` shorthand property value. - * - * @param {string} value - * @return {object} Collection font-family nodes - */ -export default function findAnimationName(value) { - const animationNames = [] - - const valueNodes = postcssValueParser(value) - - // Handle `inherit`, `initial` and etc - if (valueNodes.nodes.length === 1 && basicKeywords.has(valueNodes.nodes[0].value.toLowerCase())) { - return [valueNodes.nodes[0]] - } - - valueNodes.walk((valueNode) => { - if (valueNode.type === "function") { return false } - if (valueNode.type !== "word") { return } - - const valueLowerCase = valueNode.value.toLowerCase() - - // Ignore non standard syntax - if (!isStandardSyntaxValue(valueLowerCase)) { return } - // Ignore variables - if (isVariable(valueLowerCase)) { return } - // Ignore keywords for other font parts - if (animationShorthandKeywords.has(valueLowerCase)) { return } - // Ignore numbers with units - const unit = getUnitFromValueNode(valueNode) - if (unit || unit === "") { return } - - animationNames.push(valueNode) - }) - - return animationNames -} diff --git a/src/utils/findListStyleType.js b/src/utils/findListStyleType.js deleted file mode 100644 index 43b1fbf276..0000000000 --- a/src/utils/findListStyleType.js +++ /dev/null @@ -1,47 +0,0 @@ -import { - isStandardSyntaxValue, - isVariable, -} from "./" -import { - listStyleImageKeywords, - listStylePositionKeywords, - listStyleTypeKeywords, -} from "../reference/keywordSets" -import postcssValueParser from "postcss-value-parser" - -/** - * Get the list-style-type within a `list-style` shorthand property value. - * - * @param {string} value - * @return {object} Collection list-style-type nodes - */ -export default function findListStyleType(value) { - const listStyleTypes = [] - - const valueNodes = postcssValueParser(value) - - // Handle `inherit`, `initial` and etc - if (valueNodes.nodes.length === 1 - && listStyleTypeKeywords.has(valueNodes.nodes[0].value.toLowerCase()) - ) { - return [valueNodes.nodes[0]] - } - - valueNodes.walk((valueNode) => { - if (valueNode.type === "function") { return false } - if (valueNode.type !== "word") { return } - - const valueLowerCase = valueNode.value.toLowerCase() - - // Ignore non standard syntax - if (!isStandardSyntaxValue(valueLowerCase)) { return } - // Ignore variables - if (isVariable(valueLowerCase)) { return } - // Ignore keywords for other font parts - if (listStylePositionKeywords.has(valueLowerCase) || listStyleImageKeywords.has(valueLowerCase)) { return } - - listStyleTypes.push(valueNode) - }) - - return listStyleTypes -} diff --git a/src/utils/getIsFileIgnored.js b/src/utils/getIsFileIgnored.js deleted file mode 100644 index 3ffbb5d84c..0000000000 --- a/src/utils/getIsFileIgnored.js +++ /dev/null @@ -1,15 +0,0 @@ -import ignore from "ignore" -import multimatch from "multimatch" -import path from "path" - -export default function getIsFileIgnored(ignorePatterns, ignoreFiles) { - const ignorePatternsFilter = ignore().add(ignorePatterns).createFilter() - - return (file) => { - const filepathRelativeToCwd = path.relative(process.cwd(), file) - return ( - (ignorePatternsFilter && !ignorePatternsFilter(filepathRelativeToCwd)) - || (ignoreFiles && multimatch(file, ignoreFiles).length) - ) - } -} diff --git a/src/utils/getModulePath.js b/src/utils/getModulePath.js deleted file mode 100644 index c13fe3c779..0000000000 --- a/src/utils/getModulePath.js +++ /dev/null @@ -1,20 +0,0 @@ -import { configurationError } from "." -import resolveFrom from "resolve-from" - -export default function ( - basedir: string, - lookup: string, -): string { - // First try to resolve from the provided directory, - // then try to resolve from process.cwd. - let path = resolveFrom(basedir, lookup) - if (!path) { - path = resolveFrom(process.cwd(), lookup) - } - if (!path) { - throw configurationError( - `Could not find "${lookup}". Do you need a \`configBasedir\`?` - ) - } - return path -} diff --git a/src/utils/getUnitFromValueNode.js b/src/utils/getUnitFromValueNode.js deleted file mode 100644 index d159c9e7e8..0000000000 --- a/src/utils/getUnitFromValueNode.js +++ /dev/null @@ -1,35 +0,0 @@ -import blurInterpolation from "./blurInterpolation" -import { isFinite } from "lodash" -import isStandardSyntaxValue from "./isStandardSyntaxValue" -import valueParser from "postcss-value-parser" - -/** - * Get unit from value node - * - * Returns `null` if the unit is not found. - * - * @param {node} node - * @return {string|null} - */ -export default function (node) { - if (!node || (node && !node.value)) { return null } - - const value = blurInterpolation(node.value, "") - // ignore hack unit - .replace("\\0", "") - .replace("\\9", "") - // ignore decimal place - .replace(".", "") - - if (node.type !== "word" - || !isStandardSyntaxValue(value) - || !isFinite(parseInt(value)) - || node.value[0] === "#" - ) { return null } - - const parsedUnit = valueParser.unit(value) - - if (!parsedUnit) { return null } - - return parsedUnit.unit -} diff --git a/src/utils/hasEmptyLine.js b/src/utils/hasEmptyLine.js deleted file mode 100644 index d9d983de4a..0000000000 --- a/src/utils/hasEmptyLine.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Check if a string contains at least one empty line - * - * @param {string} input - * @return {boolean} - */ -export default function (string) { - return string - && ( - string.indexOf("\n\n") !== -1 - || string.indexOf("\n\r\n") !== -1 - ) -} diff --git a/src/utils/hasInterpolation.js b/src/utils/hasInterpolation.js deleted file mode 100644 index c712346428..0000000000 --- a/src/utils/hasInterpolation.js +++ /dev/null @@ -1,17 +0,0 @@ -import { - hasLessInterpolation, - hasPsvInterpolation, - hasScssInterpolation, -} from "../utils" -/** - * Check whether a string has interpolation - * - * @param {string} string - * @return {boolean} If `true`, a string has interpolation - */ -export default function (string) { - // SCSS or Less interpolation - if (hasLessInterpolation(string) || hasScssInterpolation(string) || hasPsvInterpolation(string)) { return true } - - return false -} diff --git a/src/utils/index.js b/src/utils/index.js deleted file mode 100644 index 4bc7b590b7..0000000000 --- a/src/utils/index.js +++ /dev/null @@ -1,59 +0,0 @@ -export { default as atRuleParamIndex } from "./atRuleParamIndex" -export { default as beforeBlockString } from "./beforeBlockString" -export { default as blockString } from "./blockString" -export { default as blurComments } from "./blurComments" -export { default as blurFunctionArguments } from "./blurFunctionArguments" -export { default as blurInterpolation } from "./blurInterpolation" -export { default as configurationError } from "./configurationError" -export { default as containsString } from "./containsString" -export { default as declarationValueIndex } from "./declarationValueIndex" -export { default as findAnimationName } from "./findAnimationName" -export { default as findAtRuleContext } from "./findAtRuleContext" -export { default as findFontFamily } from "./findFontFamily" -export { default as findListStyleType } from "./findListStyleType" -export { default as functionArgumentsSearch } from "./functionArgumentsSearch" -export { default as getUnitFromValueNode } from "./getUnitFromValueNode" -export { default as hasBlock } from "./hasBlock" -export { default as hasEmptyBlock } from "./hasEmptyBlock" -export { default as hasEmptyLine } from "./hasEmptyLine" -export { default as hasInterpolation } from "./hasInterpolation" -export { default as hasLessInterpolation } from "./hasLessInterpolation" -export { default as hasPsvInterpolation } from "./hasPsvInterpolation" -export { default as hasScssInterpolation } from "./hasScssInterpolation" -export { default as isAutoprefixable } from "./isAutoprefixable" -export { default as isCounterIncrementCustomIdentValue } from "./isCounterIncrementCustomIdentValue" -export { default as isCustomMediaQuery } from "./isCustomMediaQuery" -export { default as isCustomProperty } from "./isCustomProperty" -export { default as isCustomPropertySet } from "./isCustomPropertySet" -export { default as isKeyframeRule } from "./isKeyframeRule" -export { default as isKeyframeSelector } from "./isKeyframeSelector" -export { default as isNumbery } from "./isNumbery" -export { default as isOnlyWhitespace } from "./isOnlyWhitespace" -export { default as isRangeContextMediaFeature } from "./isRangeContextMediaFeature" -export { default as isSingleLineString } from "./isSingleLineString" -export { default as isStandardSyntaxAtRule } from "./isStandardSyntaxAtRule" -export { default as isStandardSyntaxDeclaration } from "./isStandardSyntaxDeclaration" -export { default as isStandardSyntaxFunction } from "./isStandardSyntaxFunction" -export { default as isStandardSyntaxMediaFeature } from "./isStandardSyntaxMediaFeature" -export { default as isStandardSyntaxMediaFeatureName } from "./isStandardSyntaxMediaFeatureName" -export { default as isStandardSyntaxProperty } from "./isStandardSyntaxProperty" -export { default as isStandardSyntaxRule } from "./isStandardSyntaxRule" -export { default as isStandardSyntaxSelector } from "./isStandardSyntaxSelector" -export { default as isStandardSyntaxTypeSelector } from "./isStandardSyntaxTypeSelector" -export { default as isStandardSyntaxUrl } from "./isStandardSyntaxUrl" -export { default as isStandardSyntaxValue } from "./isStandardSyntaxValue" -export { default as isValidFontSize } from "./isValidFontSize" -export { default as isValidHex } from "./isValidHex" -export { default as isVariable } from "./isVariable" -export { default as isWhitespace } from "./isWhitespace" -export { default as matchesStringOrRegExp } from "./matchesStringOrRegExp" -export { default as nextNonCommentNode } from "./nextNonCommentNode" -export { default as nodeContextLookup } from "./nodeContextLookup" -export { default as optionsMatches } from "./optionsMatches" -export { default as parseSelector } from "./parseSelector" -export { default as rawNodeString } from "./rawNodeString" -export { default as report } from "./report" -export { default as ruleMessages } from "./ruleMessages" -export { default as validateOptions } from "./validateOptions" -export { default as whitespaceChecker } from "./whitespaceChecker" -export { default as getModulePath } from "./getModulePath" diff --git a/src/utils/isCounterIncrementCustomIdentValue.js b/src/utils/isCounterIncrementCustomIdentValue.js deleted file mode 100644 index 9e47b59a80..0000000000 --- a/src/utils/isCounterIncrementCustomIdentValue.js +++ /dev/null @@ -1,17 +0,0 @@ -import { counterIncrementKeywords } from "../reference/keywordSets" -import { isFinite } from "lodash" - -/** - * Check value is a custom ident - * - * @param {string} value - * @return {boolean} If `true`, value is a custom ident - */ - -export default function (value) { - const valueLowerCase = value.toLowerCase() - - if (counterIncrementKeywords.has(valueLowerCase) || isFinite(parseInt(valueLowerCase))) { return false } - - return true -} diff --git a/src/utils/isCustomPropertySet.js b/src/utils/isCustomPropertySet.js deleted file mode 100644 index cbea8a6ee9..0000000000 --- a/src/utils/isCustomPropertySet.js +++ /dev/null @@ -1,14 +0,0 @@ -import _ from "lodash" -import { hasBlock } from "../utils" - -/** - * Check whether a property is a custom properties - * - * @param {string} rule - * @return {boolean} If `true`, property is a custom properties - */ -export default function (rule) { - const selector = _.get(rule, "raws.selector.raw", rule.selector) - - return (rule.type === "rule" && hasBlock(rule) && selector.slice(0, 2) === "--" && selector.slice(-1) === ":") -} diff --git a/src/utils/isKeyframeRule.js b/src/utils/isKeyframeRule.js deleted file mode 100644 index ac0e1ee4fc..0000000000 --- a/src/utils/isKeyframeRule.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Check if a rule is a keyframe one - * - * @param {Rule} rule - postcss rule node - * @return {boolean} If `true`, the rule is a keyframe one - */ -export default function (rule) { - const { parent } = rule - return ( - parent.type === "atrule" - && parent.name.toLowerCase() === "keyframes" - ) -} diff --git a/src/utils/isKeyframeSelector.js b/src/utils/isKeyframeSelector.js deleted file mode 100644 index 5d181dea6b..0000000000 --- a/src/utils/isKeyframeSelector.js +++ /dev/null @@ -1,16 +0,0 @@ -import { keyframeSelectorKeywords } from "../reference/keywordSets" - -/** - * Check whether a string is a keyframe selector. - * - * @param {string} selector - * @return {boolean} If `true`, the selector is a keyframe selector - */ -export default function (selector) { - if (keyframeSelectorKeywords.has(selector)) { return true } - - // Percentages - if (/^(?:\d+\.?\d*|\d*\.?\d+)%$/.test(selector)) { return true } - - return false -} diff --git a/src/utils/isRangeContextMediaFeature.js b/src/utils/isRangeContextMediaFeature.js deleted file mode 100644 index 7e9a11416e..0000000000 --- a/src/utils/isRangeContextMediaFeature.js +++ /dev/null @@ -1,13 +0,0 @@ -/** - * Check whether a media feature is a range context one - * - * @param {string} media feature - * @return {boolean} If `true`, media feature is a range context one - */ -export default function (mediaFeature) { - return ( - mediaFeature.indexOf("=") !== -1 - || mediaFeature.indexOf("<") !== -1 - || mediaFeature.indexOf(">") !== -1 - ) -} diff --git a/src/utils/isStandardSyntaxSelector.js b/src/utils/isStandardSyntaxSelector.js deleted file mode 100644 index 2da53839f1..0000000000 --- a/src/utils/isStandardSyntaxSelector.js +++ /dev/null @@ -1,16 +0,0 @@ -import { hasInterpolation } from "../utils" -/** - * Check whether a selector is standard - * - * @param {string} selector - * @return {boolean} If `true`, the selector is standard - */ -export default function (selector) { - // SCSS or Less interpolation - if (hasInterpolation(selector)) { return false } - - // SCSS placeholder selectors - if (selector.indexOf("%") === 0) { return false } - - return true -} diff --git a/src/utils/isStandardSyntaxUrl.js b/src/utils/isStandardSyntaxUrl.js deleted file mode 100644 index e9503b6a42..0000000000 --- a/src/utils/isStandardSyntaxUrl.js +++ /dev/null @@ -1,42 +0,0 @@ -import { - hasLessInterpolation, - hasPsvInterpolation, - hasScssInterpolation, -} from "../utils" - -/** - * Check whether a URL is standard - * - * @param {string} url - * @return {boolean} If `true`, the url is standard - */ -export default function (url) { - if (url.length === 0) { return true } - - // Sass interpolation works anywhere - if (hasScssInterpolation(url) || hasPsvInterpolation(url)) { return false } - - // Inside `'` and `"` work only LESS interpolation - if ((url[0] === "'" && url[url.length - 1] === "'") - || (url[0] === "\"" && url[url.length - 1] === "\"") - ) { - if (hasLessInterpolation(url)) { return false } - - return true - } - - // Less variable works only at the beginning - // Check is less variable, allow use '@url/some/path' - // https://github.com/less/less.js/blob/3.x/lib/less/parser/parser.js#L547 - if (url[0] === "@" && /^@@?[\w-]+$/.test(url)) { return false } - - // In url without quotes scss variable can be everywhere - // But in this case it is allowed to use only specific characters - // Also forbidden "/" at the end of url - if (url.indexOf("$") !== -1 - && (/^[\$\sA-Za-z0-9+-/*_'"\/]+$/).test(url) - && url[url.length - 1] !== "/" - ) { return false } - - return true -} diff --git a/src/utils/isStandardSyntaxValue.js b/src/utils/isStandardSyntaxValue.js deleted file mode 100644 index 3d064c476e..0000000000 --- a/src/utils/isStandardSyntaxValue.js +++ /dev/null @@ -1,19 +0,0 @@ -import { hasInterpolation } from "../utils" -/** - * Check whether a value is standard - * - * @param {string} value - * @return {boolean} If `true`, the value is a variable - */ -export default function (value) { - // SCSS variable - if (value[0] === "$") { return false } - - // Less variable - if (value[0] === "@") { return false } - - // SCSS or Less interpolation - if (hasInterpolation(value)) { return false } - - return true -} diff --git a/src/utils/isValidFontSize.js b/src/utils/isValidFontSize.js deleted file mode 100644 index a31dca2d18..0000000000 --- a/src/utils/isValidFontSize.js +++ /dev/null @@ -1,26 +0,0 @@ -import { - fontSizeKeywords, - lengthUnits, -} from "../reference/keywordSets" -import valueParser from "postcss-value-parser" - -/** - * Check if a word is a font-size value. - * - * @param {string} word - * @return {boolean} - */ -export default function (word) { - if (!word) { return false } - - if (fontSizeKeywords.has(word)) { return true } - - const numberUnit = valueParser.unit(word) - if (!numberUnit) { return false } - - const { unit } = numberUnit - if (unit === "%") { return true } - if (lengthUnits.has(unit.toLowerCase())) { return true } - - return false -} diff --git a/src/utils/parseSelector.js b/src/utils/parseSelector.js deleted file mode 100644 index b0ee09c85c..0000000000 --- a/src/utils/parseSelector.js +++ /dev/null @@ -1,9 +0,0 @@ -import selectorParser from "postcss-selector-parser" - -export default function (selector, result, node, cb) { - try { - selectorParser(cb).process(selector) - } catch (e) { - result.warn("Cannot parse selector", { node }) - } -} diff --git a/system-tests/001/001.test.js b/system-tests/001/001.test.js index 3682beb5f9..ae680d152e 100644 --- a/system-tests/001/001.test.js +++ b/system-tests/001/001.test.js @@ -1,6 +1,7 @@ /* @flow */ -import * as systemTestUtils from "../systemTestUtils" -import stylelint from "../../src" +"use strict" +const systemTestUtils = require("../systemTestUtils") +const stylelint = require("../../lib") it("001", () => { return stylelint.lint({ diff --git a/system-tests/002/002.test.js b/system-tests/002/002.test.js index 2040b06f18..b6d8283f1b 100644 --- a/system-tests/002/002.test.js +++ b/system-tests/002/002.test.js @@ -1,6 +1,7 @@ /* @flow */ -import * as systemTestUtils from "../systemTestUtils" -import stylelint from "../../src" +"use strict" +const systemTestUtils = require("../systemTestUtils") +const stylelint = require("../../lib") it("002", () => { return stylelint.lint({ diff --git a/system-tests/003/003.test.js b/system-tests/003/003.test.js index 464ad58cf1..6f1e18fd63 100644 --- a/system-tests/003/003.test.js +++ b/system-tests/003/003.test.js @@ -1,6 +1,7 @@ /* @flow */ -import * as systemTestUtils from "../systemTestUtils" -import stylelint from "../../src" +"use strict" +const systemTestUtils = require("../systemTestUtils") +const stylelint = require("../../lib") it("003", () => { return stylelint.lint({ diff --git a/system-tests/systemTestUtils.js b/system-tests/systemTestUtils.js index fd88c2b026..0c0471687a 100644 --- a/system-tests/systemTestUtils.js +++ b/system-tests/systemTestUtils.js @@ -1,20 +1,22 @@ -import _ from "lodash" -import path from "path" -import stripAnsi from "strip-ansi" +"use strict" -export function caseFilePath(caseNumber, fileName) { +const _ = require("lodash") +const path = require("path") +const stripAnsi = require("strip-ansi") + +function caseFilePath(caseNumber, fileName) { return path.join(__dirname, caseNumber, fileName) } -export function caseStylesheetGlob(caseNumber) { +function caseStylesheetGlob(caseNumber) { return caseFilePath(caseNumber, "stylesheet.*") } -export function caseConfig(caseNumber, ext = "json") { +function caseConfig(caseNumber, ext = "json") { return caseFilePath(caseNumber, `config.${ext}`) } -export function prepResults(results) { +function prepResults(results) { return results.map((result) => { // The _postcssResult object is not part of our API and is huge const preppedResult = _.omit(result, "_postcssResult") @@ -26,6 +28,14 @@ export function prepResults(results) { }) } -export function stripColors(input) { +function stripColors(input) { return stripAnsi(input) } + +module.exports = { + caseFilePath, + caseStylesheetGlob, + caseConfig, + prepResults, + stripColors, +}