├── .cirrus.star ├── .cirrus.yml ├── .cirrus ├── nodejs-10.Dockerfile └── nodejs-10.jdk17.Dockerfile ├── .github ├── CODEOWNERS └── workflows │ └── release.yml ├── .gitignore ├── .gitmodules ├── LICENSE ├── README.md ├── its ├── plugin │ ├── pom.xml │ ├── projects │ │ ├── (dir with paren) │ │ │ └── src │ │ │ │ └── file1.css │ │ ├── external-report-project │ │ │ ├── report.json │ │ │ ├── src │ │ │ │ ├── file1.css │ │ │ │ └── file2.css │ │ │ └── stylelintconfig.json │ │ ├── issues-project │ │ │ └── src │ │ │ │ ├── cssModules.css │ │ │ │ ├── empty1.css │ │ │ │ ├── empty2.less │ │ │ │ ├── empty3.scss │ │ │ │ ├── file-with-parsing-error-excluded.css │ │ │ │ ├── file-with-parsing-error.css │ │ │ │ ├── file-with-parsing-error.less │ │ │ │ ├── file1.css │ │ │ │ ├── file2.less │ │ │ │ ├── file3.scss │ │ │ │ ├── file4.foo │ │ │ │ ├── file5-1.html │ │ │ │ ├── file5.htm │ │ │ │ ├── file6.vue │ │ │ │ └── file7.jsx │ │ ├── metrics-project │ │ │ └── src │ │ │ │ ├── file1.css │ │ │ │ ├── file2.less │ │ │ │ ├── file3.scss │ │ │ │ └── file4.html │ │ ├── minified-project │ │ │ └── src │ │ │ │ ├── normal.css │ │ │ │ ├── normal.min.css │ │ │ │ └── oneline.css │ │ └── php-project │ │ │ └── src │ │ │ └── index.php │ └── src │ │ └── test │ │ └── java │ │ └── org │ │ └── sonar │ │ └── css │ │ └── its │ │ ├── IssuesTest.java │ │ ├── MetricsTest.java │ │ ├── MinifiedTest.java │ │ ├── NoCssFileProjectTest.java │ │ ├── NonStandardPathTest.java │ │ ├── StylelintReportTest.java │ │ └── Tests.java ├── pom.xml ├── ruling │ ├── pom.xml │ └── src │ │ └── test │ │ ├── java │ │ └── org │ │ │ └── sonar │ │ │ └── css │ │ │ └── its │ │ │ └── CssRulingTest.java │ │ └── resources │ │ └── expected │ │ ├── css-S4647.json │ │ ├── css-S4648.json │ │ ├── css-S4653.json │ │ ├── css-S4654.json │ │ ├── css-S4656.json │ │ ├── css-S4658.json │ │ ├── css-S4661.json │ │ ├── css-S4662.json │ │ ├── css-S4664.json │ │ ├── css-S4666.json │ │ ├── css-S4667.json │ │ └── css-S4670.json └── sources │ └── custom │ ├── S4653.scss │ ├── S4654.css │ ├── S4654.scss │ ├── S4660.scss │ ├── S4662.scss │ ├── S4666.css │ ├── S4670.css │ ├── S5362.less │ ├── S5362.scss │ └── test.css ├── pom.xml ├── sonar-css-plugin ├── css-bundle │ ├── bin │ │ └── server │ ├── jest.config.js │ ├── package.json │ ├── package │ │ ├── .gitignore │ │ ├── node_modules │ │ │ ├── .bin │ │ │ │ └── run-node │ │ │ ├── .yarn-integrity │ │ │ └── run-node │ │ │ │ ├── license │ │ │ │ ├── package.json │ │ │ │ ├── readme.md │ │ │ │ └── run-node │ │ ├── package.json │ │ └── yarn.lock │ ├── src │ │ └── server.ts │ ├── tests │ │ ├── fixtures │ │ │ ├── file-bom.css │ │ │ ├── file.css │ │ │ ├── file.html │ │ │ ├── file.php │ │ │ └── stylelintconfig.json │ │ ├── server-mock-stylelint.test.ts │ │ ├── server.test.ts │ │ └── utils.ts │ ├── tsconfig.json │ └── yarn.lock ├── pom.xml └── src │ ├── main │ ├── java │ │ └── org │ │ │ └── sonar │ │ │ └── css │ │ │ └── plugin │ │ │ ├── CssLanguage.java │ │ │ ├── CssPlugin.java │ │ │ ├── CssRuleSensor.java │ │ │ ├── CssRules.java │ │ │ ├── CssRulesDefinition.java │ │ │ ├── MinifiedFilesFilter.java │ │ │ ├── SonarWayProfile.java │ │ │ ├── StylelintReport.java │ │ │ ├── StylelintReportSensor.java │ │ │ ├── metrics │ │ │ ├── CssLexer.java │ │ │ ├── CssToken.java │ │ │ ├── CssTokenType.java │ │ │ ├── MetricSensor.java │ │ │ └── Tokenizer.java │ │ │ ├── package-info.java │ │ │ ├── rules │ │ │ ├── AtRuleNoUnknown.java │ │ │ ├── BlockNoEmpty.java │ │ │ ├── ColorNoInvalidHex.java │ │ │ ├── CommentNoEmpty.java │ │ │ ├── CssRule.java │ │ │ ├── DeclarationBlockNoDuplicateProperties.java │ │ │ ├── DeclarationBlockNoShorthandPropertyOverrides.java │ │ │ ├── FontFamilyNoDuplicateNames.java │ │ │ ├── FontFamilyNoMissingGenericFamilyKeyword.java │ │ │ ├── FunctionCalcNoInvalid.java │ │ │ ├── FunctionCalcNoUnspacedOperator.java │ │ │ ├── FunctionLinearGradientNoNonstandardDirection.java │ │ │ ├── KeyframeDeclarationNoImportant.java │ │ │ ├── MediaFeatureNameNoUnknown.java │ │ │ ├── NoDescendingSpecificity.java │ │ │ ├── NoDuplicateAtImportRules.java │ │ │ ├── NoDuplicateSelectors.java │ │ │ ├── NoEmptySource.java │ │ │ ├── NoExtraSemicolons.java │ │ │ ├── NoInvalidDoubleSlashComments.java │ │ │ ├── PropertyNoUnknown.java │ │ │ ├── RuleUtils.java │ │ │ ├── SelectorPseudoClassNoUnknown.java │ │ │ ├── SelectorPseudoElementNoUnknown.java │ │ │ ├── SelectorTypeNoUnknown.java │ │ │ ├── StringNoNewline.java │ │ │ ├── UnitNoUnknown.java │ │ │ └── package-info.java │ │ │ └── server │ │ │ ├── CssAnalyzerBridgeServer.java │ │ │ ├── NetUtils.java │ │ │ ├── NodeDeprecationWarning.java │ │ │ ├── bundle │ │ │ ├── Bundle.java │ │ │ ├── CssAnalyzerBundle.java │ │ │ ├── Zip.java │ │ │ └── package-info.java │ │ │ └── package-info.java │ └── resources │ │ ├── org │ │ └── sonar │ │ │ └── l10n │ │ │ └── css │ │ │ └── rules │ │ │ ├── css │ │ │ ├── S1116.html │ │ │ ├── S1116.json │ │ │ ├── S1128.html │ │ │ ├── S1128.json │ │ │ ├── S4647.html │ │ │ ├── S4647.json │ │ │ ├── S4648.html │ │ │ ├── S4648.json │ │ │ ├── S4649.html │ │ │ ├── S4649.json │ │ │ ├── S4650.html │ │ │ ├── S4650.json │ │ │ ├── S4651.html │ │ │ ├── S4651.json │ │ │ ├── S4652.html │ │ │ ├── S4652.json │ │ │ ├── S4653.html │ │ │ ├── S4653.json │ │ │ ├── S4654.html │ │ │ ├── S4654.json │ │ │ ├── S4655.html │ │ │ ├── S4655.json │ │ │ ├── S4656.html │ │ │ ├── S4656.json │ │ │ ├── S4657.html │ │ │ ├── S4657.json │ │ │ ├── S4658.html │ │ │ ├── S4658.json │ │ │ ├── S4659.html │ │ │ ├── S4659.json │ │ │ ├── S4660.html │ │ │ ├── S4660.json │ │ │ ├── S4661.html │ │ │ ├── S4661.json │ │ │ ├── S4662.html │ │ │ ├── S4662.json │ │ │ ├── S4663.html │ │ │ ├── S4663.json │ │ │ ├── S4664.html │ │ │ ├── S4664.json │ │ │ ├── S4666.html │ │ │ ├── S4666.json │ │ │ ├── S4667.html │ │ │ ├── S4667.json │ │ │ ├── S4668.html │ │ │ ├── S4668.json │ │ │ ├── S4670.html │ │ │ ├── S4670.json │ │ │ ├── S5362.html │ │ │ ├── S5362.json │ │ │ └── Sonar_way_profile.json │ │ │ └── stylelint │ │ │ └── rules.json │ │ └── static │ │ └── documentation.md │ ├── sonarcss-assembly.xml │ └── test │ ├── java │ └── org │ │ └── sonar │ │ └── css │ │ └── plugin │ │ ├── CssLanguageTest.java │ │ ├── CssPluginTest.java │ │ ├── CssRuleSensorTest.java │ │ ├── CssRulesDefinitionTest.java │ │ ├── MinifiedFilesFilterTest.java │ │ ├── SonarWayProfileTest.java │ │ ├── StylelintReportSensorTest.java │ │ ├── TestActiveRules.java │ │ ├── metrics │ │ ├── MetricSensorTest.java │ │ └── TokenizerTest.java │ │ ├── rules │ │ └── CssRuleTest.java │ │ └── server │ │ ├── CssAnalyzerBridgeServerTest.java │ │ ├── NetUtilsTest.java │ │ ├── NodeDeprecationWarningTest.java │ │ └── bundle │ │ ├── CssAnalyzerBundleTest.java │ │ └── ZipTest.java │ └── resources │ ├── bundle │ ├── invalid-zip-file.zip │ └── test-css-bundle.zip │ ├── mock-start-server │ ├── failedClose.js │ ├── startServer.js │ ├── testLogs.js │ └── throw.js │ └── stylelint-report │ ├── file.css │ ├── invalid-file.json │ ├── report-utf16.json │ ├── report-utf8-bom.json │ └── report.json ├── sonarpedia.json ├── third-party-licenses.sh ├── wss-unified-agent.config └── yarn.lock /.cirrus.star: -------------------------------------------------------------------------------- 1 | load("github.com/SonarSource/cirrus-modules@v2", "load_features") 2 | 3 | def main(ctx): 4 | return load_features(ctx) 5 | -------------------------------------------------------------------------------- /.cirrus/nodejs-10.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG CIRRUS_AWS_ACCOUNT=275878209202 2 | FROM ${CIRRUS_AWS_ACCOUNT}.dkr.ecr.eu-central-1.amazonaws.com/base:j11-latest 3 | 4 | USER root 5 | 6 | ENV NODE_VERSION v10.23.2 7 | 8 | RUN wget -U "nodejs" -q -O nodejs.tar.gz https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.gz \ 9 | && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 --no-same-owner \ 10 | && rm nodejs.tar.gz \ 11 | && ln -s /usr/local/bin/node /usr/local/bin/nodejs 12 | 13 | ENV YARN_VERSION 1.22.5 14 | 15 | RUN curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \ 16 | && mkdir -p /opt \ 17 | && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \ 18 | && ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \ 19 | && ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \ 20 | && rm yarn-v$YARN_VERSION.tar.gz 21 | 22 | 23 | USER sonarsource 24 | -------------------------------------------------------------------------------- /.cirrus/nodejs-10.jdk17.Dockerfile: -------------------------------------------------------------------------------- 1 | ARG CIRRUS_AWS_ACCOUNT=275878209202 2 | FROM ${CIRRUS_AWS_ACCOUNT}.dkr.ecr.eu-central-1.amazonaws.com/base:j17-latest 3 | 4 | USER root 5 | 6 | ENV NODE_VERSION v10.23.2 7 | 8 | RUN wget -U "nodejs" -q -O nodejs.tar.gz https://nodejs.org/dist/${NODE_VERSION}/node-${NODE_VERSION}-linux-x64.tar.gz \ 9 | && tar -xzf "nodejs.tar.gz" -C /usr/local --strip-components=1 --no-same-owner \ 10 | && rm nodejs.tar.gz \ 11 | && ln -s /usr/local/bin/node /usr/local/bin/nodejs 12 | 13 | ENV YARN_VERSION 1.22.5 14 | 15 | RUN curl -fsSLO --compressed "https://yarnpkg.com/downloads/$YARN_VERSION/yarn-v$YARN_VERSION.tar.gz" \ 16 | && mkdir -p /opt \ 17 | && tar -xzf yarn-v$YARN_VERSION.tar.gz -C /opt/ \ 18 | && ln -s /opt/yarn-v$YARN_VERSION/bin/yarn /usr/local/bin/yarn \ 19 | && ln -s /opt/yarn-v$YARN_VERSION/bin/yarnpkg /usr/local/bin/yarnpkg \ 20 | && rm yarn-v$YARN_VERSION.tar.gz 21 | 22 | 23 | USER sonarsource 24 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | .github/CODEOWNERS @SonarSource/languages-team-jsts 2 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: sonar-release 3 | # This workflow is triggered when publishing a new github release 4 | # yamllint disable-line rule:truthy 5 | on: 6 | release: 7 | types: 8 | - published 9 | 10 | jobs: 11 | release: 12 | permissions: 13 | id-token: write 14 | contents: write 15 | uses: SonarSource/gh-action_release/.github/workflows/main.yaml@v5 16 | with: 17 | publishToBinaries: true 18 | mavenCentralSync: true 19 | slackChannel: team-lang-js-ts-css 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ---- Mac OS X 2 | .DS_Store 3 | Icon? 4 | # Thumbnails 5 | ._* 6 | # Files that might appear on external disk 7 | .Spotlight-V100 8 | .Trashes 9 | 10 | # ---- Windows 11 | # Windows image file caches 12 | Thumbs.db 13 | # Folder config file 14 | Desktop.ini 15 | 16 | # ---- IntelliJ IDEA 17 | *.iws 18 | *.iml 19 | *.ipr 20 | .idea/ 21 | out/ 22 | 23 | # ---- Eclipse 24 | .project 25 | .settings/ 26 | .classpath 27 | 28 | # --- SonarQube 29 | .sonar/ 30 | .sonarlint/ 31 | .scannerwork/ 32 | 33 | # --- Gradle 34 | .gradle/ 35 | build/ 36 | 37 | # --- Maven and Orchestrator 38 | target/ 39 | 40 | # npm 41 | node_modules/ 42 | 43 | # Visual Studio 44 | .vs/ 45 | 46 | # CSS-bundle 47 | sonar-css-plugin/css-bundle/test-report.xml 48 | sonar-css-plugin/css-bundle/coverage/ 49 | sonar-css-plugin/css-bundle/lib/ 50 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "its/sources/projects"] 2 | path = its/sources/projects 3 | url = https://github.com/SonarSource/css-test-sources.git 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DEPRECATED 2 | This project is deprecated. Now analysis for CSS code is provided by [SonarJS](https://github.com/SonarSource/SonarJS) analyzer. 3 | ## Code Quality and Security for CSS 4 | 5 | ### Building 6 | 7 | ```bash 8 | mvn package 9 | ``` 10 | 11 | ### Feedback 12 | Please use https://community.sonarsource.com/ to provide any kind of feedback about CSS analysis in SonarQube/SonarCloud/SonarLint. 13 | ### License 14 | 15 | Copyright 2018-2021 SonarSource. 16 | 17 | Licensed under the [GNU Lesser General Public License, Version 3.0](http://www.gnu.org/licenses/lgpl.txt) 18 | -------------------------------------------------------------------------------- /its/plugin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | its 7 | org.sonarsource.css 8 | 1.4.3-SNAPSHOT 9 | 10 | 4.0.0 11 | 12 | plugin 13 | 14 | 15 | 16 | 17 | maven-surefire-plugin 18 | 19 | 20 | **/Tests.java 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | org.sonarsource.orchestrator 30 | sonar-orchestrator 31 | 32 | 33 | org.sonarsource.sonarqube 34 | sonar-ws 35 | ${sonar.version} 36 | test 37 | 38 | 39 | org.assertj 40 | assertj-core 41 | 42 | 43 | org.sonarsource.css 44 | sonar-css-plugin 45 | ${version} 46 | test 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /its/plugin/projects/(dir with paren)/src/file1.css: -------------------------------------------------------------------------------- 1 | @import "a.css"; 2 | @import "a.css"; /* S1128 | no-duplicate-at-import-rules */ 3 | -------------------------------------------------------------------------------- /its/plugin/projects/external-report-project/report.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "source": "src/file1.css", 4 | "deprecations": [], 5 | "invalidOptionWarnings": [], 6 | "parseErrors": [], 7 | "errored": true, 8 | "warnings": [ 9 | { 10 | "line": 111, 11 | "column": 1, 12 | "rule": "no-missing-end-of-source-newline", 13 | "severity": "error", 14 | "text": "Unexpected missing end-of-source newline (no-missing-end-of-source-newline)" 15 | }, 16 | { 17 | "line": 81, 18 | "column": 1, 19 | "rule": "rule-empty-line-before", 20 | "severity": "error", 21 | "text": "Expected empty line before rule (rule-empty-line-before)" 22 | }, 23 | { 24 | "line": 55, 25 | "column": 20, 26 | "rule": "selector-pseudo-element-colon-notation", 27 | "severity": "error", 28 | "text": "Expected double colon pseudo-element notation (selector-pseudo-element-colon-notation)" 29 | } 30 | ] 31 | }, 32 | { 33 | "source": "src/file2.css", 34 | "deprecations": [], 35 | "invalidOptionWarnings": [], 36 | "parseErrors": [], 37 | "errored": true, 38 | "warnings": [ 39 | { 40 | "line": 58, 41 | "column": 28, 42 | "rule": "block-no-empty", 43 | "severity": "error", 44 | "text": "Unexpected empty block (block-no-empty)" 45 | }, 46 | { 47 | "line": 114, 48 | "column": 1, 49 | "rule": "no-missing-end-of-source-newline", 50 | "severity": "error", 51 | "text": "Unexpected missing end-of-source newline (no-missing-end-of-source-newline)" 52 | } 53 | ] 54 | } 55 | ] 56 | -------------------------------------------------------------------------------- /its/plugin/projects/external-report-project/src/file1.css: -------------------------------------------------------------------------------- 1 | .navbar-search { 2 | position: relative; 3 | padding: calc((var(--globalNavHeight) - var(--globalNavContentHeight)) / 2) 0; 4 | } 5 | 6 | .navbar-search .search-box, 7 | .navbar-search .search-box-input { 8 | width: 26vw; 9 | max-width: 310px; 10 | min-width: 260px; 11 | height: var(--globalNavContentHeight); 12 | } 13 | 14 | .navbar-search .search-box-input { 15 | border-color: #fff; 16 | } 17 | 18 | .navbar-search .search-box-note { 19 | line-height: calc(var(--globalNavContentHeight) - 2px); 20 | } 21 | 22 | .navbar-search .search-box-magnifier, 23 | .navbar-search .search-box-clear { 24 | top: calc((var(--globalNavContentHeight) - 16px) / 2); 25 | } 26 | 27 | .navbar-search-input { 28 | vertical-align: middle; 29 | width: 310px; 30 | margin-top: 3px; 31 | margin-bottom: 3px; 32 | padding-left: 26px !important; 33 | } 34 | 35 | .navbar-search-input-hint { 36 | position: absolute; 37 | top: 1px; 38 | right: 27px; 39 | line-height: var(--controlHeight); 40 | font-size: var(--smallFontSize); 41 | color: var(--secondFontColor); 42 | } 43 | 44 | .navbar-search-icon { 45 | position: relative; 46 | z-index: var(--aboveNormalZIndex); 47 | vertical-align: middle; 48 | width: 16px; 49 | margin-left: 4px; 50 | margin-right: -20px; 51 | background-color: #fff; 52 | color: var(--secondFontColor); 53 | } 54 | 55 | .navbar-search-icon:before { 56 | font-size: var(--mediumFontSize); 57 | } 58 | 59 | .navbar-search-item-link { 60 | display: flex !important; 61 | } 62 | 63 | .navbar-search-item-match { 64 | flex-grow: 5; 65 | overflow: hidden; 66 | text-overflow: ellipsis; 67 | } 68 | 69 | .navbar-search-item-right { 70 | flex-grow: 1; 71 | padding-left: 10px; 72 | text-align: right; 73 | } 74 | 75 | .navbar-search-item-icons { 76 | position: relative; 77 | flex-shrink: 0; 78 | width: 16px; 79 | height: 16px; 80 | } 81 | .navbar-search-item-icons > * { 82 | position: absolute; 83 | z-index: 5; 84 | top: 0; 85 | left: 0; 86 | } 87 | 88 | .navbar-search-item-icons > .icon-outline, 89 | .navbar-search-item-icons > .icon-clock { 90 | z-index: 6; 91 | top: -4px; 92 | left: -5px; 93 | } 94 | 95 | .navbar-search-no-results { 96 | margin-top: 4px; 97 | padding: 5px 10px; 98 | } 99 | 100 | .global-navbar-search-dropdown { 101 | top: 100% !important; 102 | max-height: 80vh; 103 | width: 440px; 104 | padding: 0 !important; 105 | overflow-y: auto; 106 | overflow-x: hidden; 107 | } 108 | 109 | .global-navbar-search-dropdown .dropdown-bottom-hint { 110 | margin-bottom: 0; 111 | } -------------------------------------------------------------------------------- /its/plugin/projects/external-report-project/src/file2.css: -------------------------------------------------------------------------------- 1 | .date-input-control { 2 | position: relative; 3 | display: inline-block; 4 | cursor: pointer; 5 | } 6 | 7 | .date-input-control-input { 8 | width: 130px; 9 | padding-left: var(--controlHeight) !important; 10 | cursor: pointer; 11 | } 12 | 13 | .date-input-control-input.is-filled { 14 | padding-right: 16px !important; 15 | } 16 | 17 | .date-input-control-icon { 18 | position: absolute; 19 | top: 4px; 20 | left: 4px; 21 | } 22 | 23 | .date-input-control-icon path { 24 | fill: var(--gray80); 25 | transition: fill 0.3s ease; 26 | } 27 | 28 | .date-input-control-input:focus + .date-input-control-icon path { 29 | fill: var(--blue); 30 | } 31 | 32 | .date-input-control-reset { 33 | position: absolute; 34 | top: 4px; 35 | right: 4px; 36 | border: none; 37 | } 38 | 39 | .date-input-calendar { 40 | position: absolute; 41 | z-index: var(--dropdownMenuZIndex); 42 | top: 100%; 43 | left: 0; 44 | border: 1px solid var(--barBorderColor); 45 | background-color: #fff; 46 | box-shadow: var(--defaultShadow); 47 | } 48 | 49 | .date-input-calendar-nav { 50 | display: flex; 51 | justify-content: space-between; 52 | align-items: center; 53 | padding-top: var(--gridSize); 54 | padding-left: var(--gridSize); 55 | padding-right: var(--gridSize); 56 | } 57 | 58 | .date-input-calender-month { 59 | } 60 | 61 | .date-input-calender-month-select { 62 | width: 70px; 63 | } 64 | 65 | .button.boolean-toggle { 66 | display: inline-block; 67 | vertical-align: middle; 68 | width: 48px; 69 | height: var(--controlHeight); 70 | padding: 1px; 71 | border: 1px solid var(--gray80); 72 | border-radius: var(--controlHeight); 73 | box-sizing: border-box; 74 | background-color: #fff; 75 | cursor: pointer; 76 | transition: all 0.3s ease; 77 | } 78 | 79 | .button.boolean-toggle:hover { 80 | background-color: #fff; 81 | } 82 | 83 | .button.boolean-toggle:focus { 84 | border-color: var(--blue); 85 | background-color: #f6f6f6; 86 | } 87 | 88 | .boolean-toggle-handle { 89 | width: 20px; 90 | height: 20px; 91 | border: 1px solid var(--gray80); 92 | border-radius: 22px; 93 | box-sizing: border-box; 94 | background-color: #f6f6f6; 95 | transition: transform 0.3s cubic-bezier(0.87, -0.41, 0.19, 1.44), border 0.3s ease; 96 | } 97 | 98 | .button.boolean-toggle-on { 99 | border-color: var(--darkBlue); 100 | background-color: var(--darkBlue); 101 | } 102 | 103 | .button.boolean-toggle-on:hover { 104 | background-color: var(--darkBlue); 105 | } 106 | 107 | .button.boolean-toggle-on:focus { 108 | background-color: var(--darkBlue); 109 | } 110 | 111 | .button.boolean-toggle-on .boolean-toggle-handle { 112 | border-color: #f6f6f6; 113 | transform: translateX(var(--controlHeight)); 114 | } -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/cssModules.css: -------------------------------------------------------------------------------- 1 | /* Adding one standard issue to make sure we analyze this file */ 2 | @unknown { /* S4662 | at-rule-no-unknown */ 3 | width: 1px; 4 | } 5 | 6 | /* ignored by S4662 | at-rule-no-unknown */ 7 | @value colors: "./colors.css"; 8 | @value blue, red, green from colors; 9 | 10 | .className { 11 | color: green; 12 | background: red; 13 | } 14 | .otherClassName { 15 | /* ignored by S4654 | property-no-unknown */ 16 | composes: className; 17 | color: yellow; 18 | } 19 | 20 | /* ignored by S4659 | selector-pseudo-class-no-unknown */ 21 | :export { 22 | /* ignored by S4654 | property-no-unknown */ 23 | exportedKey: exportedValue; 24 | /* ... */ 25 | } 26 | 27 | /* ignored by S4659 | selector-pseudo-class-no-unknown */ 28 | :import("path/to/dep.css") { 29 | /* ignored by S4654 | property-no-unknown */ 30 | localAlias: keyFromDep; 31 | /* ... */ 32 | } 33 | -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/empty1.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSource/sonar-css/0885b299dc850766eae254cebc6d8de85f4957ab/its/plugin/projects/issues-project/src/empty1.css -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/empty2.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSource/sonar-css/0885b299dc850766eae254cebc6d8de85f4957ab/its/plugin/projects/issues-project/src/empty2.less -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/empty3.scss: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SonarSource/sonar-css/0885b299dc850766eae254cebc6d8de85f4957ab/its/plugin/projects/issues-project/src/empty3.scss -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/file-with-parsing-error-excluded.css: -------------------------------------------------------------------------------- 1 | a { 2 | -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/file-with-parsing-error.css: -------------------------------------------------------------------------------- 1 | a { 2 | -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/file-with-parsing-error.less: -------------------------------------------------------------------------------- 1 | .foo(@i : 1 ) when (@i <= 10){ 2 | @num : @i *10 ; 3 | .height-@{num}{ 4 | height : @num * 1%; 5 | } 6 | } 7 | 8 | -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/file2.less: -------------------------------------------------------------------------------- 1 | @import "a.css"; 2 | @import "a.css"; /* S1128 | no-duplicate-at-import-rules */ 3 | 4 | b a { 5 | color: pink;; /* S1116 | no-extra-semicolons */ 6 | } 7 | 8 | a { /* S4664 | no-descending-specificity */ 9 | color: red; 10 | } 11 | 12 | a::pseudo { /* S4660 | selector-pseudo-element-no-unknown */ 13 | color: red; 14 | } 15 | 16 | a:unknown { /* S4659 | selector-pseudo-class-no-unknown */ 17 | background-color: #ffw; /* S4647 | color-no-invalid-hex */ 18 | /* */ /* S4663 | comment-no-empty */ 19 | content: "first 20 | second"; /* S4652 | string-no-newline */ 21 | color: pink; 22 | color: pink; /* S4656 | declaration-block-no-duplicate-properties */ 23 | color: orange; /* S4656 | declaration-block-no-duplicate-properties | Not raised because property has a different value */ 24 | font: 1em/1.3 Times; /* S4649 | font-family-no-missing-generic-family-keyword */ 25 | font-family: serif, serif; /* S4648 | font-family-no-duplicate-names */ 26 | heigth: 100%; /* S4654 | property-no-unknown */ 27 | padding-left: 10px; 28 | padding: 20px; /* S4657 | declaration-block-no-shorthand-property-overrides */ 29 | top: calc(1px+2px); /* S4650 | function-calc-no-unspaced-operator */ /* S4653 | unit-no-unknown */ 30 | width: calc(100% 80px); /* S5362 | function-calc-no-invalid */ 31 | } 32 | 33 | // color: pink; /* S4668 | no-invalid-double-slash-comments | Doesn't raise for LESS */ 34 | 35 | .class1 { 36 | width: 100px; 37 | background: linear-gradient(top, #fff, #000); /* S4651 | function-linear-gradient-no-nonstandard-direction */ 38 | } 39 | 40 | .class1 { /* S4666 | no-duplicate-selectors */ 41 | padding: 100px; 42 | } 43 | 44 | unknown { /* S4670 | selector-type-no-unknown */ 45 | color: black; 46 | } 47 | 48 | @unknown { /* S4662 | at-rule-no-unknown */ 49 | width: 1px; 50 | } 51 | 52 | @keyframes important1 { 53 | from { 54 | margin-top: 50px; 55 | } 56 | to { 57 | margin-top: 100px !important; /* S4655 | keyframe-declaration-no-important */ 58 | } 59 | } 60 | 61 | .class2 { } /* S4658 | block-no-empty */ 62 | 63 | @media screen and (unknown) { /* S4661 | media-feature-name-no-unknown */ 64 | width: 2px; 65 | } 66 | -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/file3.scss: -------------------------------------------------------------------------------- 1 | @import "a.css"; 2 | @import "a.css"; /* S1128 | no-duplicate-at-import-rules */ 3 | 4 | b a { 5 | color: pink;; /* S1116 | no-extra-semicolons */ 6 | } 7 | 8 | a { /* S4664 | no-descending-specificity */ 9 | color: red; 10 | } 11 | 12 | a::pseudo { /* S4660 | selector-pseudo-element-no-unknown */ 13 | color: red; 14 | } 15 | 16 | a:unknown { /* S4659 | selector-pseudo-class-no-unknown */ 17 | background-color: #ffw; /* S4647 | color-no-invalid-hex */ 18 | /* */ /* S4663 | comment-no-empty */ 19 | content: "first 20 | second"; /* S4652 | string-no-newline */ 21 | color: pink; 22 | color: pink; /* S4656 | declaration-block-no-duplicate-properties */ 23 | color: orange; /* S4656 | declaration-block-no-duplicate-properties | Not raised because property has a different value */ 24 | font: 1em/1.3 Times; /* S4649 | font-family-no-missing-generic-family-keyword */ 25 | font-family: serif, serif; /* S4648 | font-family-no-duplicate-names */ 26 | heigth: 100%; /* S4654 | property-no-unknown */ 27 | padding-left: 10px; 28 | padding: 20px; /* S4657 | declaration-block-no-shorthand-property-overrides */ 29 | top: calc(1px+2px); /* S4650 | function-calc-no-unspaced-operator */ /* S4653 | unit-no-unknown */ 30 | width: calc(100% 80px); /* S5362 | function-calc-no-invalid */ 31 | } 32 | 33 | // color: pink; /* S4668 | no-invalid-double-slash-comments | Doesn't raise for SCSS */ 34 | 35 | .class1 { 36 | width: 100px; 37 | background: linear-gradient(top, #fff, #000); /* S4651 | function-linear-gradient-no-nonstandard-direction */ 38 | } 39 | 40 | .class1 { /* S4666 | no-duplicate-selectors */ 41 | padding: 100px; 42 | } 43 | 44 | unknown { /* S4670 | selector-type-no-unknown */ 45 | color: black; 46 | } 47 | 48 | @unknown { /* S4662 | at-rule-no-unknown */ 49 | width: 1px; 50 | } 51 | 52 | @keyframes important1 { 53 | from { 54 | margin-top: 50px; 55 | } 56 | to { 57 | margin-top: 100px !important; /* S4655 | keyframe-declaration-no-important */ 58 | } 59 | } 60 | 61 | .class2 { } /* S4658 | block-no-empty */ 62 | 63 | @media screen and (unknown) { /* S4661 | media-feature-name-no-unknown */ 64 | width: 2px; 65 | } 66 | 67 | @mixin adjust-location($x, $y) { 68 | @if unitless($x) { 69 | color: blue; 70 | @debug ""; 71 | @warn ""; 72 | @error ""; 73 | @at-root [dir="ltr"] { color: blue; } 74 | } @else { 75 | color: black; 76 | } 77 | } 78 | 79 | @for $i from 1 through 3 { 80 | .item-#{$i} { width: 2em * $i; } 81 | } 82 | 83 | @each $header, $size in (h1: 2em, h2: 1.5em, h3: 1.2em) { 84 | #{$header} { 85 | font-size: $size; 86 | @include large-text; 87 | } 88 | } 89 | 90 | $i: 6; 91 | @while $i > 0 { 92 | .item-#{$i} { width: 2em * $i; } 93 | $i: $i - 2; 94 | } 95 | -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/file4.foo: -------------------------------------------------------------------------------- 1 | File required to test execution of analysis on a file with non-css extension 2 | -------------------------------------------------------------------------------- /its/plugin/projects/issues-project/src/file5-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
Hello World!
Extra semicolons are usually introduced by mistake, for example because:
;;
Having the import of the same file twice, makes one of them useless. Leaving them in reduces the code's readability, since their presence can be 2 | confusing.
5 | @import 'a.css'; 6 | @import 'a.css'; // Noncompliant 7 | 8 | @import url("a.css"); 9 | @import url("a.css"); // Noncompliant 10 |
This rule ignores @import in less files.
@import
less
An invalid color definition will by default be interpreted as black, which is likely to have unintended impacts on the expected look and feel of 2 | the website.
This rule raises an issue when a color definition (color, background-color) is not valid. The color definition is 4 | considered valid when it is made of hexadecimal characters:
color
background-color
11 | a { 12 | color: #3c; /* Noncompliant; shorthand should be made of 3 characters */ 13 | } 14 | div { 15 | background-color: #3cb371a; /* Noncompliant; alpha should have 2 characters */ 16 | } 17 |
20 | a { 21 | color: #3cc; 22 | } 23 | div { 24 | background-color: #3cb371ac; 25 | } 26 |
Having duplicated font names doesn't help to read the font declaration and may be an indicator the author of the line was not sure how to configure 2 | it. This rule raises an issue when font or font-family properties contain a duplicated font name. This rule ignores 3 | $sass, @less, and var(--custom-property) variable syntaxes.
font
font-family
$sass
@less
var(--custom-property)
6 | a { 7 | font-family: 'Georgia', Georgia, serif; /* Noncompliant; 'Georgia' is duplicated */ 8 | } 9 |
12 | a { 13 | font-family: Georgia, serif; 14 | } 15 |
If none of the font names defined in a font or font-family declaration are available on the browser of the user, the 2 | browser will display the text using its default font. It's recommended to always define a generic font family for each declaration of 3 | font or font-family to get a less degraded situation than relying on the default browser font. All browsers should implement 4 | a list of generic font matching these families: Serif, Sans-serif, cursive, fantasy, 5 | Monospace.
Serif
Sans-serif
cursive
fantasy
Monospace
8 | a { 9 | font-family: Helvetica, Arial, Verdana, Tahoma; /* Noncompliant; there is no generic font family in the list */ 10 | } 11 |
14 | a { 15 | font-family: Helvetica, Arial, Verdana, Tahoma, sans-serif; 16 | } 17 |
calc is a CSS3 function that provides the possibility to do simple math in CSS (add, subtract, divide, multiply). Without spaces 2 | around operators, calc will have no effect.
calc
More precisely, before an operator, there must be a single whitespace or a newline plus indentation. After an operator, there must be a single 4 | whitespace or a newline.
7 | #div1 { 8 | position: absolute; 9 | width: calc(100%- 100px); /* Noncompliant; no space after the % sign */ 10 | } 11 |
14 | #div1 { 15 | position: absolute; 16 | width: calc(100% - 100px); 17 | } 18 |
linear-gradient was standardized with CSS3. Before that, it was possible to use different non-standard values to define the gradient's 2 | direction. Because these values are not standard, they are not supported in all browsers and therefore they should no longer be used in order to get 3 | the expected gradient in the latest browser versions that support CSS3.
linear-gradient
This rule raises an issue when the first parameter of a linear-gradient is not a valid <side-or-corner> or 5 | angle.
<side-or-corner>
angle
8 | .foo { 9 | background: -webkit-linear-gradient(to top, #fff, #000); 10 | background: linear-gradient(top, #fff, #000); 11 | } 12 | 13 | .bar { 14 | background: linear-gradient(45, #fff, #000); 15 | } 16 |
19 | .foo { 20 | background: -webkit-linear-gradient(top, #fff, #000); 21 | background: linear-gradient(to top, #fff, #000); 22 | } 23 | 24 | .bar { 25 | background: linear-gradient(45deg, #fff, #000); 26 | } 27 |
According to the W3C specifications:
3 | A string cannot directly contain a newline. To include a newline in a string, use an escape representing the line feed character in ISO-10646 4 | (U+000A), such as "\A" or "\00000a". 5 | [...] 6 | It is possible to break strings over several lines, for aesthetic or other reasons, but in such a case the newline itself has to be escaped with 7 | a backslash (\). 8 |
A string cannot directly contain a newline. To include a newline in a string, use an escape representing the line feed character in ISO-10646 4 | (U+000A), such as "\A" or "\00000a".
[...]
It is possible to break strings over several lines, for aesthetic or other reasons, but in such a case the newline itself has to be escaped with 7 | a backslash (\).
11 | a { 12 | content: "first 13 | second"; 14 | } 15 |
18 | a { 19 | content: "first\Asecond"; 20 | } 21 |
The W3C specifications define the units that can be used with lengths. A unit that is not part of the list of supported ones is likely 2 | to be a typo and will be seen as a UI bug by the user.
units
This rule raises an issue each time a unit is not officially supported.
6 | a { 7 | width: 10pixels; /* Noncompliant; "pixels" is not a valid unit */ 8 | } 9 |
12 | a { 13 | width: 10px; 14 | } 15 |
The W3C specifications define the valid CSS properties. Only the official and browser-specific properties should be used to get the expected impact 2 | in the final rendering.
This rule ignores:
-moz-align-self
-webkit-align-self
10 | a { 11 | colour: blue; /* Noncompliant; colour is not part of the specifications */ 12 | } 13 |
16 | a { 17 | color: blue; 18 | } 19 |
!important within keyframes declarations is completely ignored in some browsers and therefore it should not be used to be consistent 2 | among all browsers.
!important
5 | @keyframes kf { 6 | from { margin-top: 50px; } 7 | 50% { margin-top: 150px !important; } /* Noncompliant; ignored */ 8 | to { margin-top: 100px; } 9 | } 10 |
13 | @keyframes kf { 14 | from { margin-top: 50px; } 15 | 50% { margin-top: 150px; } 16 | to { margin-top: 100px; } 17 | } 18 |
CSS allows duplicate property names but only the last instance of a duplicated name determines the actual value that will be used for it. 2 | Therefore, changing values of other occurrences of a duplicated name will have no effect and may cause misunderstandings and bugs.
This rule ignores $sass, @less, and var(--custom-property) variable syntaxes.
6 | a { 7 | color: pink; 8 | background: orange; 9 | color: orange 10 | } 11 |
14 | a { 15 | color: pink; 16 | background: orange 17 | } 18 |
A shorthand property defined after a longhand property will completely override the value defined in the longhand property making the longhand one 2 | useless. The code should be refactored to consider the longhand property or to remove it completely.
5 | a { 6 | padding-left: 10px; 7 | padding: 20px; /* Noncompliant; padding is overriding padding-left making it useless */ 8 | } 9 |
12 | a { 13 | padding: 10px; /* Compliant; padding is defining a general behaviour and padding-left, just after, is precising the left case */ 14 | padding-left: 20px; 15 | } 16 |
Leftover empty blocks are usually introduced by mistake. They are useless and prevent readability of the code. They should be removed or completed 2 | with real code.
5 | a { } 6 |
9 | a { color: pink; } 10 |
The W3C specifications define the valid pseudo-class selectors. Only the official and browser-specific pseudo-class selectors should be used to get 2 | the expected impact in the final rendering.
5 | a:hoverr { /* Noncompliant; there is a typo on the word "hover" */ 6 | ... 7 | } 8 |
11 | a:hover { 12 | ... 13 | } 14 |
The W3C specifications define the valid pseudo-element selectors. Only the official and browser-specific pseudo-element selectors should be used to 2 | get the expected impact in the final rendering.
5 | a::beforre { /* Noncompliant; there is a typo on the word "before" */ 6 | ... 7 | } 8 |
11 | a::before { 12 | ... 13 | } 14 |
The W3C specifications define the valid media features. Only the official and browser-specific media features should be used to get the expected 2 | impact in the final rendering.
5 | @media screen and (unknown: 1000px) { .. } 6 |
9 | @media screen and (width: 1000px) { .. } 10 |
@media
The W3C specifications define the valid at-rules. Only the official and browser-specific at-rules should be used to get 2 | the expected impact in the final rendering.
at-rules
5 | @encoding "utf-8"; 6 |
9 | @charset "utf-8"; 10 |
An empty multi-line comment is likely to be a mistake and doesn't help to improve the readability of the code. For these reasons, it should be 2 | removed.
5 | /* */ 6 | 7 | /* 8 | 9 | */ 10 |
Order of instructions in CSS is important: instructions with equal specificity that occur later in the file take the priority. But when a selector 2 | with a higher specificity (e.g. p a { color: green;}) comes before the selector it overrides (e.g.: a { color: green;}), the 3 | priority is given to the first one. Even if it works properly, this is harder to anticipate the behaviour of the stylesheet while reading as it goes 4 | against the principle that the last instruction takes the priority.
p a { color: green;}
a { color: green;}
7 | p a { 8 | color: green; 9 | } 10 | 11 | a { 12 | color: blue; 13 | } 14 |
17 | a { 18 | color: blue; 19 | } 20 | 21 | p a { 22 | color: green; 23 | } 24 |
Duplication of selectors might indicate a copy-paste mistake. The rule detects the following kinds of duplications:
8 | .foo, .bar, .foo { ... } /* Noncompliant */ 9 | 10 | .class1 { ... } 11 | .class1 { ... } /* Noncompliant */ 12 |
15 | .foo, .bar { ... } 16 | 17 | .class1 { ... } 18 | .class2 { ... } 19 |
This rule raises an issue when a CSS file is empty (ie: containing only spaces).
The W3C specifications say comments should be defined using /* ... */. The use of // is not supported on all browsers and 2 | can lead to unexpected results.
/* ... */
//
5 | // some comment 6 | a { color: pink; } 7 |
10 | /* some comment */ 11 | a { color: pink; } 12 |
This rule ignores single line comments in less and scss files.
scss
HTML, SVG, and MathML define the selectors which can be used in a CSS. A selector that is not part of them is likely to be a typo or a 2 | misunderstanding of the CSS syntax.
5 | field {} 6 | 7 | ul list {} 8 |
11 | input {} 12 | 13 | ul li {} 14 |
To perform calculations when specifying a CSS property calc() function can be used. This function takes single expression as 2 | parameter. When writing this expression some rules must be respected:
calc()
Otherwise calc() function will be invalid and the entire rule using it will be ignored.
12 | .btn { 13 | border: solid black 1px; 14 | width: calc(100% 80px); /* Noncompliant */ 15 | } 16 |
19 | .btn { 20 | border: solid black 1px; 21 | width: calc(100% - 80px); 22 | } 23 |