diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000..b656be27 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,17 @@ +# EditorConfig is awesome: http://EditorConfig.org +root = true + +[*] +charset = utf-8 +indent_size = 2 +indent_style = space +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[{.gitignore,.npmignore}] +end_of_line = +trim_trailing_whitespace = false + +[*.md] +trim_trailing_whitespace = false diff --git a/.eslintrc.js b/.eslintrc.js new file mode 100644 index 00000000..eccc6811 --- /dev/null +++ b/.eslintrc.js @@ -0,0 +1,334 @@ +module.exports = { + 'env': { + 'browser': true, + 'commonjs': true, + 'es6': true + }, + 'extends': 'eslint:recommended', + 'globals': { + 'Atomics': 'readonly', + 'SharedArrayBuffer': 'readonly' + }, + 'parserOptions': { + 'ecmaVersion': 2018 + }, + 'rules': { + 'accessor-pairs': 'error', + 'array-bracket-newline': 'error', + 'array-bracket-spacing': [ + 'error', + 'never' + ], + 'array-callback-return': 'off', + 'array-element-newline': 'off', + 'arrow-body-style': 'off', + 'arrow-parens': 'off', + 'arrow-spacing': [ + 'error', + { + 'after': true, + 'before': true + } + ], + 'block-scoped-var': 'error', + 'block-spacing': 'error', + 'brace-style': [ + 'error', + '1tbs' + ], + 'callback-return': 'error', + 'camelcase': 'error', + 'capitalized-comments': 'off', + 'class-methods-use-this': 'off', + 'comma-dangle': 'error', + 'comma-spacing': [ + 'error', + { + 'after': true, + 'before': false + } + ], + 'comma-style': [ + 'error', + 'last' + ], + 'complexity': 'error', + 'computed-property-spacing': [ + 'error', + 'never' + ], + 'consistent-return': 'off', + 'consistent-this': 'error', + 'curly': 'off', + 'default-case': 'error', + 'dot-location': [ + 'error', + 'property' + ], + 'dot-notation': [ + 'error', + { + 'allowKeywords': true + } + ], + 'eol-last': 'error', + 'eqeqeq': 'error', + 'func-call-spacing': 'error', + 'func-name-matching': 'error', + 'func-names': 'off', + 'func-style': 'off', + 'function-paren-newline': 'error', + 'generator-star-spacing': 'error', + 'global-require': 'off', + 'guard-for-in': 'off', + 'handle-callback-err': 'error', + 'id-blacklist': 'error', + 'id-length': 'off', + 'id-match': 'error', + 'implicit-arrow-linebreak': [ + 'error', + 'beside' + ], + 'indent': 'off', + 'indent-legacy': 'off', + 'init-declarations': 'off', + 'jsx-quotes': 'error', + 'key-spacing': 'error', + 'keyword-spacing': [ + 'error', + { + 'after': true, + 'before': true + } + ], + 'line-comment-position': 'off', + 'linebreak-style': [ + 'error', + 'unix' + ], + 'lines-around-comment': 'off', + 'lines-around-directive': 'error', + 'lines-between-class-members': [ + 'error', + 'always' + ], + 'max-classes-per-file': 'error', + 'max-depth': 'error', + 'max-len': 'error', + 'max-lines': 'off', + 'max-lines-per-function': 'error', + 'max-nested-callbacks': 'error', + 'max-params': 'error', + 'max-statements': 'off', + 'max-statements-per-line': 'error', + 'multiline-comment-style': [ + 'error', + 'separate-lines' + ], + 'multiline-ternary': 'off', + 'new-cap': 'error', + 'new-parens': 'error', + 'newline-after-var': 'off', + 'newline-before-return': 'off', + 'newline-per-chained-call': 'off', + 'no-alert': 'error', + 'no-array-constructor': 'error', + 'no-async-promise-executor': 'error', + 'no-await-in-loop': 'error', + 'no-bitwise': 'error', + 'no-buffer-constructor': 'error', + 'no-caller': 'error', + 'no-catch-shadow': 'error', + 'no-confusing-arrow': 'error', + 'no-continue': 'off', + 'no-div-regex': 'error', + 'no-duplicate-imports': 'error', + 'no-else-return': 'off', + 'no-empty-function': 'error', + 'no-eq-null': 'error', + 'no-eval': 'error', + 'no-extend-native': 'error', + 'no-extra-bind': 'error', + 'no-extra-label': 'error', + 'no-extra-parens': 'error', + 'no-floating-decimal': 'error', + 'no-implicit-coercion': 'error', + 'no-implicit-globals': 'error', + 'no-implied-eval': 'error', + 'no-inline-comments': 'off', + 'no-inner-declarations': [ + 'error', + 'functions' + ], + 'no-invalid-this': 'error', + 'no-iterator': 'error', + 'no-label-var': 'error', + 'no-labels': 'error', + 'no-lone-blocks': 'error', + 'no-lonely-if': 'off', + 'no-loop-func': 'error', + 'no-magic-numbers': 'off', + 'no-misleading-character-class': 'error', + 'no-mixed-operators': 'off', + 'no-mixed-requires': 'error', + 'no-multi-assign': 'off', + 'no-multi-spaces': 'error', + 'no-multi-str': 'error', + 'no-multiple-empty-lines': 'error', + 'no-native-reassign': 'error', + 'no-negated-condition': 'off', + 'no-negated-in-lhs': 'error', + 'no-nested-ternary': 'error', + 'no-new': 'error', + 'no-new-func': 'error', + 'no-new-object': 'error', + 'no-new-require': 'error', + 'no-new-wrappers': 'error', + 'no-octal-escape': 'error', + 'no-param-reassign': 'off', + 'no-path-concat': 'off', + 'no-plusplus': 'off', + 'no-process-env': 'off', + 'no-process-exit': 'off', + 'no-proto': 'error', + 'no-prototype-builtins': 'off', + 'no-restricted-globals': 'error', + 'no-restricted-imports': 'error', + 'no-restricted-modules': 'error', + 'no-restricted-properties': 'error', + 'no-restricted-syntax': 'error', + 'no-return-assign': 'error', + 'no-return-await': 'error', + 'no-script-url': 'error', + 'no-self-compare': 'error', + 'no-sequences': 'error', + 'no-shadow': 'off', + 'no-shadow-restricted-names': 'error', + 'no-spaced-func': 'error', + 'no-sync': 'off', + 'no-tabs': 'error', + 'no-template-curly-in-string': 'error', + 'no-ternary': 'off', + 'no-throw-literal': 'error', + 'no-trailing-spaces': 'error', + 'no-undef-init': 'error', + 'no-undefined': 'off', + 'no-underscore-dangle': 'off', + 'no-unmodified-loop-condition': 'error', + 'no-unneeded-ternary': 'error', + 'no-unused-expressions': 'error', + 'no-use-before-define': 'off', + 'no-useless-call': 'error', + 'no-useless-catch': 'error', + 'no-useless-computed-key': 'error', + 'no-useless-concat': 'error', + 'no-useless-constructor': 'error', + 'no-useless-rename': 'error', + 'no-useless-return': 'error', + 'no-var': 'off', + 'no-void': 'error', + 'no-warning-comments': 'off', + 'no-whitespace-before-property': 'error', + 'no-with': 'error', + 'nonblock-statement-body-position': [ + 'error', + 'any' + ], + 'object-curly-newline': 'error', + 'object-curly-spacing': [ + 'error', + 'never' + ], + 'object-shorthand': 'off', + 'one-var': 'off', + 'one-var-declaration-per-line': 'error', + 'operator-assignment': [ + 'error', + 'always' + ], + 'operator-linebreak': [ + 'error', + 'after' + ], + 'padded-blocks': 'off', + 'padding-line-between-statements': 'error', + 'prefer-arrow-callback': 'off', + 'prefer-const': 'off', + 'prefer-destructuring': 'off', + 'prefer-named-capture-group': 'error', + 'prefer-numeric-literals': 'error', + 'prefer-object-spread': 'error', + 'prefer-promise-reject-errors': [ + 'error', + { + 'allowEmptyReject': true + } + ], + 'prefer-reflect': 'off', + 'prefer-rest-params': 'error', + 'prefer-spread': 'off', + 'prefer-template': 'off', + 'quote-props': 'off', + 'quotes': [ + 'error', + 'single' + ], + 'radix': [ + 'error', + 'as-needed' + ], + 'require-atomic-updates': 'error', + 'require-await': 'error', + 'require-jsdoc': 'off', + 'require-unicode-regexp': 'off', + 'rest-spread-spacing': 'error', + 'semi': 'error', + 'semi-spacing': [ + 'error', + { + 'after': true, + 'before': false + } + ], + 'semi-style': [ + 'error', + 'last' + ], + 'sort-imports': 'error', + 'sort-keys': 'off', + 'sort-vars': 'off', + 'space-before-blocks': 'error', + 'space-before-function-paren': 'off', + 'space-in-parens': [ + 'error', + 'never' + ], + 'space-infix-ops': 'off', + 'space-unary-ops': 'error', + 'spaced-comment': [ + 'error', + 'always' + ], + 'strict': 'error', + 'switch-colon-spacing': 'error', + 'symbol-description': 'error', + 'template-curly-spacing': [ + 'error', + 'never' + ], + 'template-tag-spacing': 'error', + 'unicode-bom': [ + 'error', + 'never' + ], + 'valid-jsdoc': 'off', + 'vars-on-top': 'off', + 'wrap-iife': 'error', + 'wrap-regex': 'error', + 'yield-star-spacing': 'error', + 'yoda': [ + 'error', + 'never' + ] + } +}; diff --git a/.gitignore b/.gitignore index 4afc62a1..c7d5d95c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +lib lib-cov node_modules test/test-coverage.html diff --git a/.jscs.json b/.jscs.json deleted file mode 100644 index 8245a9c3..00000000 --- a/.jscs.json +++ /dev/null @@ -1,77 +0,0 @@ -{ - "excludeFiles": [ - "node_modules/**", - "lib-cov/**" - ], - "requireCurlyBraces": [ - "else", - "while", - "do" - ], - "requireSpaceAfterKeywords": [ - "if", - "else", - "for", - "while", - "do", - "switch", - "return", - "catch" - ], - "requireSpacesInFunctionExpression": { "beforeOpeningCurlyBrace": true }, - "disallowSpacesInFunctionExpression": { "beforeOpeningRoundBrace": true }, - "disallowMultipleVarDecl": true, - "disallowLeftStickedOperators": [ - "?", - "+", - "-", - "/", - "*", - "=", - "==", - "===", - "!=", - "!==", - ">", - ">=", - "<", - "<=" - ], - "disallowRightStickedOperators": [ - "?", - "+", - "/", - "*", - ":", - ",", - "=", - "==", - "===", - "!=", - "!==", - ">", - ">=", - "<", - "<=" - ], - "requireRightStickedOperators": ["!"], - "requireLeftStickedOperators": [","], - "disallowImplicitTypeConversion": [ - "numeric", - "boolean", - "binary", - "string" - ], - "disallowKeywords": ["with"], - "disallowKeywordsOnNewLine": ["else", "catch"], - "requireLineFeedAtFileEnd": true, - "validateJSDoc": { - "checkParamNames": true, - "checkRedundantParams": true, - "requireParamTypes": true - }, - "requireSpacesInsideObjectBrackets": "all", - "disallowSpacesInsideArrayBrackets": true, - "disallowSpaceAfterObjectKeys": true, - "disallowQuotedKeysInObjects": true -} diff --git a/.jshint-groups.js b/.jshint-groups.js deleted file mode 100644 index 95aa4d7b..00000000 --- a/.jshint-groups.js +++ /dev/null @@ -1,35 +0,0 @@ -module.exports = { - options: { - eqeqeq: true, - evil: true, - expr: true, - forin: true, - immed: true, - indent: 4, - latedef: true, - maxdepth: 5, - maxlen: 120, - maxparams: 4, - newcap: true, - noarg: true, - noempty: true, - nonew: true, - quotmark: 'single', - trailing: true, - undef: true, - unused: true - }, - groups: { - js: { - options: { node: true }, - includes: ['lib/**/*.js'] - }, - test: { - options: { - node: true, - predef: ['describe', 'beforeEach', 'afterEach', 'it'] - }, - includes: ['test/*.js'] - } - } -}; diff --git a/.jshintrc b/.jshintrc new file mode 100644 index 00000000..4b3b2959 --- /dev/null +++ b/.jshintrc @@ -0,0 +1,16 @@ +{ + "eqeqeq": true, + "esnext": true, + "freeze": true, + "globals": { + "afterEach": false, + "beforeEach": false, + "describe": false, + "it": false + }, + "node": true, + "strict": true, + "undef": true, + "unused": true, + "-W084": true +} diff --git a/.travis.yml b/.travis.yml index 4a5a560e..432dbd02 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,9 +1,6 @@ language: node_js - node_js: - - "0.10" - - "0.11" - -matrix: - allow_failures: - - node_js: "0.11" + - '11' + - '10' + - '8' + - '6' diff --git a/CHANGELOG.md b/CHANGELOG.md index bc4558bd..f6ec7421 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,153 @@ # Changelog +## 4.3.0 - 2019-03-24 + +- Updated GPE to v4.2.4 +- Updated list of supported Node version to reflect https://nodejs.org/en/about/releases/ +- Fixed #551 +- [cli] Fixed parsing of tty-mode option as boolean + +## 4.2.0 - 2017-06-12 + +- Fixed - align for flexbox properies (#507) +- Updated - replace '\n' with os.EOL for terminal output (#524) +- Updated - vow-fs dependency (#526) +- Fixed - Add empty new line in the end of cli help text (#516) +- Fixed - Endless loop with empty files (#506) +- Added - Include all flexbox properties in config files (#509) + +## 4.1.0 - 2017-05-16 + +- Added new `--tty-mode` option for cli. +- Fixed an issue with calling gpe methods on non-existing nodes. + +## 4.0.1 - 2017-02-28 + +- Added `babel-polyfill` to dependencies. + +## 4.0.0 - 2017-02-16 + +- Added note about maintenance mode +- Updated GPE to v3.4.7, which fixed a number of errors +- Added `lines-between-rulesets` option +- Added support for stdin in cli +- For `sort-order` option, divided `$include` into `$extend`, `$include name` and `$include` + +## 3.1.7 - 2015-06-09 + +- Do not remove units from values starting from zeroes, like `0.5em` (#394) + +## 3.1.6 - 2015-06-09 +- Hotfix for issue when `unitless-zero` crashed on percentages in Less (#389) + +## 3.1.5 - 2015-05-27 +- Fixed dependencies + +## 3.1.4 - 2015-05-24 +- Fixed `space-after-opening-brace` option for empty media queries (#387) + +## 3.1.3 - 2015-05-20 +- Do not add extra space before combinators which go first in selectors (#381) + +## 3.1.2 - 2015-05-20 +- Fixed processing of hashes (#379) + +## 3.1.1 - 2015-05-20 +- Fixed `space-between-declarations` vs. media queries (#378) + +## 3.1.0 - 2015-05-19 +**Thanks to +[@1000ch](https://github.com/1000ch), +[@alexeykuzmin](https://github.com/alexeykuzmin), +[@jonrohan](https://github.com/jonrohan)** +- Used new Gonzales PE API +- Fixed nested properties (#319) +- Added support for symlinked configs +- Added support for configs with any extension + +## 3.0.4 - 2014-11-15 +**Thanks to +[@gonzalocasas](https://github.com/gonzalocasas), +[@tomByrer](https://github.com/tomByrer)** +- Improved docs +- Fixed Gonzales PE version + +## 3.0.3 - 2014-09-29 +**Thanks to +[@kizu](https://github.com/kizu), +[@necolas](https://github.com/necolas)** +- Improved README +- Added non-essential files to npmignore +- Renamed `CONTRIBUTE.md` to `CONTRIBUTING.md` +- Fixed unwanted removal of empty lines between nested rulesets (#317) + +## 3.0.2 - 2014-09-21 +- Fixed Gonzales PE version + +## 3.0.1 - 2014-08-18 +**Thanks to +[@mishanga](https://github.com/mishanga), +[@cust0dian](https://github.com/cust0dian)** +- Added `khtml` to list of vendor prefixes +- Updated docs + +## 3.0.0 - 2014-07-19 +**Thanks to +[@cvrebert](https://github.com/cvrebert), +[@filtercake](https://github.com/filtercake), +[@lefoy](https://github.com/lefoy), +[@L0stSoul](https://github.com/L0stSoul), +[@kizu](https://github.com/kizu), +[@schneyra](https://github.com/schneyra), +[@thejameskyle](https://github.com/thejameskyle), +[@vecmezoni](https://github.com/vecmezoni)** + +New: + +- Sass support +- Leftovers (#160) +- Use [CSScomb Core](https://github.com/csscomb/core) +- Plugins API (with `.use()` method) +- `sort-order-fallback` (alphabetical sort order) +- `space-before-colon` +- `space-after-colon` +- `space-before-combinator` +- `space-after-combinator` +- `space-before-selector-delimiter` +- `space-after-selector-delimiter` +- `space-before-opening-brace` +- `space-after-opening-brace` +- `space-before-closing-brace` +- `space-after-declaration` +- `tab-size` + +Removed: + +- `colon-space` +- `combinator-space` +- `rule-indent` +- `stick-brace` + +Changed: + +- Divided `process` and `detect` methods +- Made `getConfig` and `detect` static methods +- Removed "best guess" logic from options +- Excluded `bower_components/**` from processing +- Updated badges at README page +- Update Gonzales PE to v3.0 +- Various fixes + +## 2.0.5 - 2014-06-09 +- Updated dependencies (#229) + +## 2.0.4 - 2014-01-16 +- Fixed relative path to config (#164) + +## 2.0.3 - 2014-01-16 +- Documentation moved to `doc` directory +- Fixed incorrect left indent (#153) + ## 2.0.2 - 2014-01-09 - Added test coverage (#138) - Added test helpers (#147) diff --git a/CONTRIBUTE.md b/CONTRIBUTING.md similarity index 51% rename from CONTRIBUTE.md rename to CONTRIBUTING.md index 93ec3412..2f309ed1 100644 --- a/CONTRIBUTE.md +++ b/CONTRIBUTING.md @@ -1,49 +1,63 @@ # Contributing to CSSComb - +## Table of Contents + +- [Pull requests](#pull-requests) +- [For maintainers](#for-maintainers) + ## Pull requests -1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes: +#### 1. [Fork](http://help.github.com/fork-a-repo/) the project, clone your fork, and configure the remotes: + +a. Clone your fork of the repo into the current directory ```bash -# Clone your fork of the repo into the current directory git clone https://github.com//csscomb.js -# Navigate to the newly cloned directory +``` + +b. Navigate to the newly cloned directory +```bash cd csscomb.js -# Assign the original repo to a remote called `upstream` +``` + +c. Assign the original repo to a remote called `upstream` +```bash git remote add upstream https://github.com/csscomb/csscomb.js ``` -2. If you cloned a while ago, get the latest changes from upstream: +#### 2. If you cloned a while ago, get the latest changes from upstream: + ```bash git checkout dev git pull upstream dev ``` -**IMPORTANT**: We are using `dev` branch for development, not `master`. -3. Create a topic branch for your feature, change, or fix: +> **IMPORTANT**: We are using `dev` branch for development, not `master`. + +#### 3. Create a topic branch for your feature, change, or fix: + ```bash git checkout -b ``` -4. Patches and features will not be accepted without tests. - Run `npm test` to check that all tests pass after you've made changes. +#### 4. Patches and features will not be accepted without tests. +Run `npm test` to check that all tests pass after you've made changes. + +#### 5. Update the `README.md` or [docs](https://github.com/csscomb/csscomb.js/tree/master/doc) if there were corresponding changes or new options. -5. Update the `README.md` if there were corresponding changes or new options. +#### 6. Locally rebase the upstream development branch into your topic branch: -6. Locally rebase the upstream development branch into your topic branch: ```bash git pull --rebase upstream dev ``` -7. Push your topic branch up to your fork: +#### 7. Push your topic branch up to your fork: ```bash git push origin ``` -8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) to a `dev` branch with a clear title and description. +#### 8. [Open a Pull Request](https://help.github.com/articles/using-pull-requests/) to a `dev` branch with a clear title and description. - -## Maintainers +## For maintainers ### Submitting changes diff --git a/README.md b/README.md index 0015bb67..94da9d3d 100644 --- a/README.md +++ b/README.md @@ -1,14 +1,20 @@ -# CSSComb [![CSSComb](logo.png)](http://csscomb.com/) -[![Build Status](https://secure.travis-ci.org/csscomb/csscomb.js.png?branch=master)](http://travis-ci.org/csscomb/csscomb.js) +# CSScomb [![CSSComb](logo.png)](http://csscomb.com/) +[![Build Status](https://travis-ci.org/csscomb/csscomb.js.svg?branch=master)](http://travis-ci.org/csscomb/csscomb.js) +[![NPM version](https://badge.fury.io/js/csscomb.svg)](http://badge.fury.io/js/csscomb) +[![Dependency Status](https://david-dm.org/csscomb/csscomb.js.svg)](https://david-dm.org/csscomb/csscomb.js) +[![devDependency Status](https://david-dm.org/csscomb/csscomb.js/dev-status.svg)](https://david-dm.org/csscomb/csscomb.js#info=devDependencies) -CSSComb is a coding style formatter for CSS. -You can easily write your own [configuration](#configuration) to make your style sheets beautiful and consistent. +CSScomb is a coding style formatter for CSS. +You can easily write your own [configuration](doc/configuration.md) to make +your style sheets beautiful and consistent. -The main feature is [sorting properties](#sort-order) in a specific order. -It was inspired by [@miripiruni](https://github.com/miripiruni)'s [PHP-based tool](https://github.com/csscomb/csscomb) of the same name. -This is the new JavaScript version, based on the powerful CSS parser [Gonzales PE](https://github.com/tonyganch/gonzales-pe). +The main feature is [sorting properties](doc/options.md#sort-order) in a specific order. +It was inspired by [@miripiruni](https://github.com/miripiruni)'s +[PHP-based tool](https://github.com/csscomb/csscomb) of the same name. +This is the new JavaScript version, based on the powerful CSS parser +[Gonzales PE](https://github.com/tonyganch/gonzales-pe). -## Installation +## 1. Install Global installation (for use as a command-line tool): @@ -34,705 +40,38 @@ To install as a dev dependency (the package will appear in your devDependencies) npm install csscomb --save-dev ``` -## Command Line usage +## 2. [Configure](doc/configuration.md) -To run `csscomb`: +There are a number of ways to configure CSScomb: -```bash -csscomb path[ path[...]] -``` +- Use one of [predefined configs](config) +- Put `.csscomb.json` file in the project root. +- Set path to config's file +- Use `*.css` file as a template -If you installed the package locally, in the project's directory run: - -```bash -./node_modules/.bin/csscomb path[ path[...]] -``` +## 3. Use -If you run `csscomb -h`, it will show some helpful information: +### [Command Line](doc/usage-cli.md) ```bash -csscomb -h - - Usage: csscomb [options] - - Options: - - -h, --help output usage information - -V, --version output the version number - -v, --verbose verbose mode - -c, --config [path] configuration file path - -d, --detect detect mode (would return detected options) - -l, --lint in case some fixes needed returns an error +csscomb assets/css ``` -## Node.js module usage - -Besides being a great CLI, `csscomb` can be used in Node.js projects (inside -a plugin or as a dev tool): +### [Node.js module](doc/usage-node.md) ```js -// Require: var Comb = require('csscomb'); - -// Configure: -var comb = new Comb(); -comb.configure(config); - -// Use: -comb.processPath('style.css'); -``` - -### configure(config) - -You must configure csscomb before use. The config must be valid JSON. -See [configuration section](#configuration) for more information. - -### processPath(path) - -Comb a file or a directory. -Warning: This method rewrites the file. - -```js -// One file: -comb.processPath('main.scss'); - -// Whole directory: -comb.processPath('assets/styles'); -``` - -### processDirectory(path) - -Comb all supported files in a directory. -Warning: This method rewrites the files. - -```js -comb.processDirectory('public/css'); -``` - -### processFile(path) - -Comb one file. -If file's syntax is not supported, the file will be ignored. -Warning: This method rewrites the file. - -```js -comb.processFile('print.less'); -``` - -### processString(text, syntax, filename) - -Comb a stylesheet. -If syntax is not `css`, you should pass a `syntax` parameter, too. -`filename` is optional. It is used to print errors. - -```js -// Comb a css string: -var css = 'a {top: 0; left: 0}'; -var combedCSS = comb.processString(css); - -// Comb a less string: -var less = '@color: tomato; a {color: @color}'; -var combedLESS = comb.processString(less, 'less'); -``` - -## Configuration - -### Through `.csscomb.json` - -`csscomb` is configured using the file [.csscomb.json](https://github.com/csscomb/csscomb.js/blob/master/config/csscomb.json), located in the project root. - -Example configuration: -```json -{ - "exclude": ["node_modules/**"], - "verbose": true, - - "always-semicolon": true, - "block-indent": " ", - "colon-space": ["", " "], - "color-case": "lower", - "color-shorthand": true, - "element-case": "lower", - "eof-newline": true, - "leading-zero": false, - "quotes": "single", - "remove-empty-rulesets": true, - "rule-indent": " ", - "stick-brace": "\n", - "strip-spaces": true, - "unitless-zero": true, - "vendor-prefix-align": true -} -``` - -**Note**: you can also use a [predefined config file](https://github.com/csscomb/csscomb.js/tree/master/config) -```bash -cp ./node_modules/csscomb/config/csscomb.json .csscomb.json -``` - -### Through `.css`-template - -Instead of configuring all the options one by one, you can use a CSS-template file: CSSComb.js will detect the coding style and use it as a config. All existing properties except `sort-order` can be configured this way. - -To provide a template just add `"template"` with the path to the template in the `.csscomb.json`: - -```json -{ - "template": "example.css" -} -``` - -CSSComb.js will only create rules based on the examples given, so if your template doesn't provide examples of usage for some of the options, or if you want to override an example, you can write them in the `.csscomb.json` after the `"template"`: - -```json -{ - "template": "example.css", - "leading-zero": false, - "vendor-prefix-align": true -} -``` - -This config would detect all the options from the `example.css`, and use `"leading-zero": false` instead of anything detected, and `"vendor-prefix-align": true` even if there were no prefixed properties or values inside the `example.css`. - -### Creating `.csscomb.json` from the `.css` file - -If you want to configure everything manually, but based on the coding style of an existing `.css`-file, you can first detect all the options using `--detect` CLI option, and then add/edit any options you like. So if you have `example.css`: - -```css -.foo -{ - width: #fff; -} -``` - -running - -```bash -csscomb -d template.css > .csscomb.json -``` - -would generate this `.csscomb.json`: - -```json -{ - "remove-empty-rulesets": true, - "always-semicolon": true, - "color-case": "lower", - "color-shorthand": true, - "strip-spaces": true, - "eof-newline": true, - "stick-brace": "\n", - "colon-space": [ - "", - " " - ], - "rule-indent": " " -} -``` - -## Options - -### exclude - -Available values: `{String[]}` - -Array of [Ant path patterns](http://ant.apache.org/manual/dirtasks.html#patterns) to exclude. - -Example: `{ "exclude": ["node_modules/**"] }` - exclude all files and directories under `node_modules` dir. - -### verbose - -Available value: `{Boolean}` `true` - -Config mode: `{ "verbose": true }` -```bash -csscomb ./test - -✓ test/integral.origin.css - test/integral.expect.css - -2 files processed -1 file fixed -96 ms spent -``` - -CLI mode: -```bash -csscomb --verbose ./test -csscomb -v ./test -``` - -### template - -**Note:** see the description of the [configuring with templates](#through-css-template). - -Available value: `{String}` path to the `.css` file. - -Example: `{ "template": "example.css" }` - -CLI mode — just provide the path to the `.css` file instead of `.csscomb.json`: -```bash -csscomb --config example.css ./test -csscomb -c example.css ./test -``` - -### always-semicolon - -Whether to add a semicolon after the last value/mixin. - -Available value: `{Boolean}` `true` - -Example: `{ "always-semicolon": true }` - -```css -/* before */ -a { color: red } - -/* after */ -a { color: red; } -``` - -Example: `{ "always-semicolon": true }` (scss file): - -```scss -// before -div { - color: tomato; - @include nani - } - -// after -div { - color: tomato; - @include nani; - } -``` - -Note that in `*.scss` and `*.less` files semicolons are not added after `}` -even if it's part of a value: - -```scss -// before -div { - color: tomato; - font: { - family: fantasy; - size: 16px - } - } - -// after -div { - color: tomato; - font: { - family: fantasy; - size: 16px; - } - } -``` - -### block-indent - -**Note**: better to use with [rule-indent](#rule-indent) - -Acceptable values: - * `{Number}` of spaces; - * `{String}` of whitespaces or tabs. If there is any other character in the - string, the value will not be set. - -Example: `{ "block-indent": 2 }` - -```css -/* before */ - a { color: red } - @media all { a { color: green } } - -/* after */ -a { color: red -} -@media all { - a { color: green - } -} -``` - -Example: `{ "block-indent": " " }` - -```css -/* before */ - a { color: red } - @media all { a { color: green } } - -/* after */ -a { color: red -} -@media all { - a { color: green - } -} -``` - - -### colon-space - -Acceptable values are of the form `{Array}` with 2 elements of the following types: - * `{Number}` of spaces; - * `{String}` of whitespaces or tabs. If there is any other character in the - string, the value will not be set. - -The first element of the array sets spaces before colon, and the second one sets -spaces after colon. - -Example: `{ "colon-space": ["\t", "\t"] }` - -```css -/* before */ -a { color: red } - -/* after */ -a { color : red } -``` - -Example: `{ "colon-space": [0, 1] }` - -```css -/* before */ -a { color:red } - -/* after */ -a { color: red } +var comb = new Comb('zen'); +comb.processPath('assets/css'); ``` +## 4. Contribute -### color-case +This project is actively mantained. But anyone and everyone is welcome to contribute. +Please take a moment to review the [guidelines for contributing](CONTRIBUTING.md). -Available values: `{String}` `lower` or `upper` - -Example: `{ "color-case": "lower" }` - -```css -/* before */ -a { color: #FFF } - -/* after */ -a { color: #fff } -``` - -### color-shorthand - -Available values: `{Boolean}` `true` or `false` - -Example: `{ "color-shorthand": true }` - -```css -/* before */ -b { color: #ffcc00 } - -/* after */ -b { color: #fc0 } -``` - -### combinator-space - -Acceptable value is `{Array}` with 2 elements of the following types: - * `{Number}` of spaces; - * `{String}` of whitespaces, tabs or new lines. If there is any other - character in the string, the value will not be set. - -The first element of the array sets spaces before combinator, and the second -one sets spaces after combinator. - -Example: `{ "combinator-space": [" ", "\n"] }` - -```css -/* before */ -a>b { color: red } - -/* after */ -a > -b { color: red } -``` - -Example: `{ "combinator-space": [1, 1] }` - -```css -/* before */ -a>b { color: red } - -/* after */ -a > b { color: red } -``` - - -### element-case - -Acceptable values: `{String}` `lower` or `upper` - -Example: `{ "element-case": "upper" }` - -```css -/* before */ -li > a { color: red } - -/* after */ -LI > A { color: red } -``` - -### eof-newline - -Acceptable values: `{Boolean}` `true` or `false` - -Example: `{ "eof-newline": true }` - -`a { color: red }` → `a { color: red }\n` - -Example: `{ "eof-newline": false }` - -`a { color: red }\n` → `a { color: red }` - -### leading-zero - -Acceptable values: `{Boolean}` `true` or `false` - -Example: `{ "leading-zero": false }` - -```css -/* before */ -p { padding: 0.5em } - -/* after */ -p { padding: .5em } -``` - -### quotes - -Acceptable values: `{String}` `single` or `double` - -Example: `{ "quotes": "single" }` - -```css -/* before */ -p[href^="https://"]:before { content: "secure" } - -/* after */ -p[href^='https://']:before { content: 'secure' } -``` - -### remove-empty-rulesets - -Acceptable values: `{Boolean}` `true` - -Example: `{ "remove-empty-rulesets": true }` - remove rulesets that have nothing but spaces. - -`a { color: red; } p { /* hey */ } b { }` → `a { color: red; } p { /* hey */ } ` - -### rule-indent - -**Note**: better to use with [block-indent](#block-indent) - -Acceptable values: - * `{Number}` of spaces; - * `{String}` of whitespaces or tabs. If there is any other character in the - string, the value will not be set. - -Example: `{ "rule-indent": 2 }` - -```css -/* before */ -a { color:red; margin:0 } - -/* after */ -a { - color:red; - margin:0 } -``` - -Example: `{ "rule-indent": " " }` - -```css -/* before */ -a { color:red; margin:0 } - -/* after */ -a { - color:red; - margin:0 } -``` - - -### sort-order - -**Note**: you can use an example of [.csscomb.json](https://github.com/csscomb/csscomb.js/blob/master/.csscomb.json) to set your own sort order - -Available values: - * `{Array}` of rules - * `{Array}` of arrays of rules for groups separation - -Example: `{ "sort-order": [ "margin", "padding" ] }` - -```css -/* before */ -p { - padding: 0; - margin: 0; -} - -/* after */ -p { - margin: 0; - padding: 0; -} -``` - -Example: `{ "sort-order": [ [ "margin", "padding" ], [ "border", "background" ] ] }` - -```css -/* before */ -p { - background: none; - border: 0; - margin: 0; - padding: 0; -} - -/* after */ -p { - margin: 0; - padding: 0; - - border: 0; - background: none; -} -``` - -If you sort properties in `*.scss` or `*.less` files, you can use one of 3 -keywords in your config: - * `$variable` for variable declarations (e.g. `$var` in Sass or `@var` in LESS); - * `$include` for included mixins (e.g. `@include ...` and `@extend ...` in Sass - or `.mixin()` in LESS); - * `$import` for `@import` rules. - -Example: `{ "sort-order": [ [ "$variable" ], [ "$include" ], [ "top", "padding" ] ] }` - -```scss -/* before */ -p { - padding: 0; - @include mixin($color); - $color: tomato; - top: 0; -} - -/* after */ -p { - $color: tomato; - - @include mixin($color); - - top: 0; - padding: 0; -} -``` - -### stick-brace - -Acceptable values: - * `{Number}` of spaces; - * `{String}` of whitespaces, tabs or newlines. If there is any other - character in the string, the value will not be set. - -Example: `{ "stick-brace": "\n" }` - -```css -/* before */ -a { color:red } - -/* after */ -a -{ color:red } -``` - -Example: `{ "stick-brace": 1 }` - -```css -/* before */ -a{ color:red } - -/* after */ -a { color:red } -``` - - -### strip-spaces - -Acceptable value: `{Boolean}` `true` - -Example: `{ "strip-spaces": true }` - -`a { color: red } \n \n \n` → `a { color: red }\n` - -`a { color: red }\t` → `a { color: red }` - -### unitless-zero - -Acceptable value: `{Boolean}` `true` - -Example: `{ "unitless-zero": true }` - -```css -/* before */ -img { border: 0px } - -/* after */ -img { border: 0 } -``` - -### vendor-prefix-align - -Acceptable value: `{Boolean}` `true` - -Example: `{ "vendor-prefix-align": true }` - -```css -/* before */ -a -{ - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - background: -webkit-linear-gradient(top, #fff 0, #eee 100%); - background: -moz-linear-gradient(top, #fff 0, #eee 100%); - background: linear-gradient(to bottom, #fff 0, #eee 100%); -} - -/* after */ -a -{ - -webkit-border-radius: 3px; - -moz-border-radius: 3px; - border-radius: 3px; - background: -webkit-linear-gradient(top, #fff 0, #eee 100%); - background: -moz-linear-gradient(top, #fff 0, #eee 100%); - background: linear-gradient(to bottom, #fff 0, #eee 100%); -} -``` - -## Tests - -Run `npm test` for tests. - -To check test coverage, you need `jscoverage` tool. -Once it is installed ([see instructions here](https://github.com/visionmedia/node-jscoverage#installation)), run: - -``` -npm run-script test-cov -open ./test/test-coverage.html -``` - -## Contributing - -Anyone and everyone is welcome to contribute. Please take a moment to -review the [guidelines for contributing](CONTRIBUTE.md). +Also you can become a mantainer. To do that please ping +[@tonyganch](https://github.com/tonyganch). ## Authors @@ -742,16 +81,23 @@ review the [guidelines for contributing](CONTRIBUTE.md). Thanks for assistance and contributions: [@miripiruni](https://github.com/miripiruni), -[@puzankov](https://github.com/puzankov), -[@L0stSoul](https://github.com/L0stSoul), +[@anton-rudeshko](https://github.com/anton-rudeshko), +[@cvrebert](https://github.com/cvrebert), +[@filtercake](https://github.com/filtercake), [@ignovak](https://github.com/ignovak), [@kizu](https://github.com/kizu), -[@anton-rudeshko](https://github.com/anton-rudeshko), -[@mishaberezin](https://github.com/mishaberezin) +[@lefoy](https://github.com/lefoy), +[@L0stSoul](https://github.com/L0stSoul), +[@mishaberezin](https://github.com/mishaberezin), +[@puzankov](https://github.com/puzankov), +[@schneyra](https://github.com/schneyra), +[@thejameskyle](https://github.com/thejameskyle), +[@vecmezoni](https://github.com/vecmezoni) ## License -This software is released under the terms of the [MIT license](https://github.com/csscomb/csscomb.js/blob/master/LICENSE). +This software is released under the terms of the +[MIT license](https://github.com/csscomb/csscomb.js/blob/master/LICENSE). ## Other projects * https://github.com/senchalabs/cssbeautify diff --git a/config/csscomb.json b/config/csscomb.json index 181aae9a..bb27a4eb 100644 --- a/config/csscomb.json +++ b/config/csscomb.json @@ -1,21 +1,27 @@ { "exclude": [ ".git/**", - "node_modules/**" + "node_modules/**", + "bower_components/**" ], "always-semicolon": true, "block-indent": " ", - "colon-space": ["", " "], "color-case": "lower", "color-shorthand": true, - "combinator-space": [" ", " "], "element-case": "lower", "eof-newline": true, "leading-zero": false, "quotes": "single", "remove-empty-rulesets": true, - "rule-indent": " ", - "stick-brace": "\n", + "space-after-colon": " ", + "space-after-combinator": " ", + "space-after-opening-brace": "\n", + "space-after-selector-delimiter": "\n", + "space-before-closing-brace": "\n", + "space-before-colon": "", + "space-before-combinator": " ", + "space-before-opening-brace": "\n", + "space-before-selector-delimiter": "", "strip-spaces": true, "unitless-zero": true, "vendor-prefix-align": true, @@ -56,10 +62,55 @@ "-ms-overflow-y", "clip", "zoom", + "-webkit-align-content", + "-ms-flex-line-pack", + "align-content", + "-webkit-box-align", + "-moz-box-align", + "-webkit-align-items", + "align-items", + "-ms-flex-align", + "-webkit-align-self", + "-ms-flex-item-align", + "-ms-grid-row-align", + "align-self", + "-webkit-box-flex", + "-webkit-flex", + "-moz-box-flex", + "-ms-flex", + "flex", + "-webkit-flex-flow", + "-ms-flex-flow", + "flex-flow", + "-webkit-flex-basis", + "-ms-flex-preferred-size", + "flex-basis", + "-webkit-box-orient", + "-webkit-box-direction", + "-webkit-flex-direction", + "-moz-box-orient", + "-moz-box-direction", + "-ms-flex-direction", "flex-direction", - "flex-order", - "flex-pack", - "flex-align" + "-webkit-flex-grow", + "-ms-flex-positive", + "flex-grow", + "-webkit-flex-shrink", + "-ms-flex-negative", + "flex-shrink", + "-webkit-flex-wrap", + "-ms-flex-wrap", + "flex-wrap", + "-webkit-box-pack", + "-moz-box-pack", + "-ms-flex-pack", + "-webkit-justify-content", + "justify-content", + "-webkit-box-ordinal-group", + "-webkit-order", + "-moz-box-ordinal-group", + "-ms-flex-order", + "order" ], [ "-webkit-box-sizing", diff --git a/config/yandex.json b/config/yandex.json index c6220544..ec6c5554 100644 --- a/config/yandex.json +++ b/config/yandex.json @@ -1,4 +1,9 @@ { + "exclude": [ + ".git/**", + "node_modules/**", + "bower_components/**" + ], "sort-order": [ [ "position", @@ -21,10 +26,55 @@ "-webkit-overflow-scrolling", "clip", "zoom", + "-webkit-align-content", + "-ms-flex-line-pack", + "align-content", + "-webkit-box-align", + "-moz-box-align", + "-webkit-align-items", + "align-items", + "-ms-flex-align", + "-webkit-align-self", + "-ms-flex-item-align", + "-ms-grid-row-align", + "align-self", + "-webkit-box-flex", + "-webkit-flex", + "-moz-box-flex", + "-ms-flex", + "flex", + "-webkit-flex-flow", + "-ms-flex-flow", + "flex-flow", + "-webkit-flex-basis", + "-ms-flex-preferred-size", + "flex-basis", + "-webkit-box-orient", + "-webkit-box-direction", + "-webkit-flex-direction", + "-moz-box-orient", + "-moz-box-direction", + "-ms-flex-direction", "flex-direction", - "flex-order", - "flex-pack", - "flex-align" + "-webkit-flex-grow", + "-ms-flex-positive", + "flex-grow", + "-webkit-flex-shrink", + "-ms-flex-negative", + "flex-shrink", + "-webkit-flex-wrap", + "-ms-flex-wrap", + "flex-wrap", + "-webkit-box-pack", + "-moz-box-pack", + "-ms-flex-pack", + "-webkit-justify-content", + "justify-content", + "-webkit-box-ordinal-group", + "-webkit-order", + "-moz-box-ordinal-group", + "-ms-flex-order", + "order" ], [ "-webkit-box-sizing", @@ -65,6 +115,10 @@ "counter-increment", "resize", "cursor", + "-webkit-user-select", + "-moz-user-select", + "-ms-user-select", + "user-select", "nav-index", "nav-up", "nav-right", @@ -140,11 +194,6 @@ "-ms-animation-iteration-count", "-o-animation-iteration-count", "animation-iteration-count", - "-webkit-animation-iteration-count", - "-moz-animation-iteration-count", - "-ms-animation-iteration-count", - "-o-animation-iteration-count", - "animation-iteration-count", "-webkit-animation-direction", "-moz-animation-direction", "-ms-animation-direction", @@ -282,15 +331,6 @@ "-webkit-box-shadow", "-moz-box-shadow", "box-shadow", - "-webkit-box-shadow", - "-moz-box-shadow", - "box-shadow", - "-webkit-box-shadow", - "-moz-box-shadow", - "box-shadow", - "-webkit-box-shadow", - "-moz-box-shadow", - "box-shadow", "filter:progid:DXImageTransform.Microsoft.gradient", "-ms-filter:\\'progid:DXImageTransform.Microsoft.gradient", "text-shadow" diff --git a/config/zen.json b/config/zen.json index 9a234aa4..9f462b96 100644 --- a/config/zen.json +++ b/config/zen.json @@ -1,4 +1,9 @@ { + "exclude": [ + ".git/**", + "node_modules/**", + "bower_components/**" + ], "sort-order": [ [ "position", "top", @@ -8,28 +13,8 @@ "z-index", "display", "visibility", - "-webkit-flex-direction", - "-moz-flex-direction", - "-ms-flex-direction", - "-o-flex-direction", - "flex-direction", - "-webkit-flex-order", - "-moz-flex-order", - "-ms-flex-order", - "-o-flex-order", - "flex-order", - "-webkit-flex-pack", - "-moz-flex-pack", - "-ms-flex-pack", - "-o-flex-pack", - "flex-pack", "float", "clear", - "-webkit-flex-align", - "-moz-flex-align", - "-ms-flex-align", - "-o-flex-align", - "flex-align", "overflow", "-ms-overflow-x", "-ms-overflow-y", @@ -37,6 +22,55 @@ "overflow-y", "-webkit-overflow-scrolling", "clip", + "-webkit-align-content", + "-ms-flex-line-pack", + "align-content", + "-webkit-box-align", + "-moz-box-align", + "-webkit-align-items", + "align-items", + "-ms-flex-align", + "-webkit-align-self", + "-ms-flex-item-align", + "-ms-grid-row-align", + "align-self", + "-webkit-box-flex", + "-webkit-flex", + "-moz-box-flex", + "-ms-flex", + "flex", + "-webkit-flex-flow", + "-ms-flex-flow", + "flex-flow", + "-webkit-flex-basis", + "-ms-flex-preferred-size", + "flex-basis", + "-webkit-box-orient", + "-webkit-box-direction", + "-webkit-flex-direction", + "-moz-box-orient", + "-moz-box-direction", + "-ms-flex-direction", + "flex-direction", + "-webkit-flex-grow", + "-ms-flex-positive", + "flex-grow", + "-webkit-flex-shrink", + "-ms-flex-negative", + "flex-shrink", + "-webkit-flex-wrap", + "-ms-flex-wrap", + "flex-wrap", + "-webkit-box-pack", + "-moz-box-pack", + "-ms-flex-pack", + "-webkit-justify-content", + "justify-content", + "-webkit-box-ordinal-group", + "-webkit-order", + "-moz-box-ordinal-group", + "-ms-flex-order", + "order", "-webkit-box-sizing", "-moz-box-sizing", "box-sizing", diff --git a/doc/configuration.md b/doc/configuration.md new file mode 100644 index 00000000..2d36135e --- /dev/null +++ b/doc/configuration.md @@ -0,0 +1,133 @@ +# Configuration + +You must configure CSScomb before use. +There are a number of ways how you can do it. + +## Use one of predefined configs + +There are [several config +files](https://github.com/csscomb/csscomb.js/tree/master/config) +included in this project you can use right away: + +- `csscomb` +- `zen` +- `yandex` + +In CLI, `csscomb` is a default config file that is used unless you provide your +own. +In Node.js, you can pass config's name to constructor: + +```js +var Comb = require('csscomb'); +var comb = new Comb('yandex'); +``` + +Feel free to use predefined configs as examples: copy one of them and modify to +your taste. +Just remember to save the file as `.csscomb.json` in project's root. + +## Create custom config + +You can easily write your own configuration. +The only requirement is that config is valid JSON in order to work correctly. + +Here is an example: + +```json +{ + "exclude": ["node_modules/**"], + "verbose": true, + + "always-semicolon": true, + "color-case": "lower", + "color-shorthand": true, + "element-case": "lower", + "eof-newline": true, + "leading-zero": false, + "quotes": "single", + "remove-empty-rulesets": true, + "strip-spaces": true, + "unitless-zero": true, + "vendor-prefix-align": true +} +``` + +Take a look at [available options](options.md) and choose those you need. You can use our [visual config builder](http://csscomb.com/config) to choose most options. + +### Where to put config + +CSScomb will look for a file named `.csscomb.json`. +The best way is to put the file in your project's root. +However, if you want to use one config for several projects, it's fine to put +the file inside a parent folder. +CSScomb will look for a config file recursively up untill it reaches your +`HOME` directory. + +Remember that you can always set custom path. +In CLI: +```bash +csscomb -c path/to/config assets/css +``` + +In Node.js: +```js +var Comb = require('csscomb'); +var config = require('path/to/config'); +var comb = new Comb(config); +``` + +## Generate config from a template + +Instead of configuring all the options one by one, you can use a template file: +CSScomb will detect the coding style and use it as a config. +All existing properties except `sort-order` can be configured this way: + +```bash +csscomb -d example.css > .csscomb.json +``` + +This will create `.csscomb.json` based on options that can be detected in +`example.css` file. + +Let's say your template file has following content: + +```css +.foo +{ + width: #fff; +} +``` + +Generated config wiil then look this way: + +```json +{ + "remove-empty-rulesets": true, + "always-semicolon": true, + "color-case": "lower", + "color-shorthand": true, + "strip-spaces": true, + "eof-newline": true +} +``` + + +## Override template's settings + +You can use template inside existing config and then complete it or override +some of detected settings: + +```json +{ + "template": "example.css", + "leading-zero": false, + "vendor-prefix-align": true +} +``` + +This config will: + +1. detect all the options from the `example.css`, +1. then use `"leading-zero": false` instead of anything detected, +1. then use `"vendor-prefix-align": true` even if there were no prefixed +properties or values inside the `example.css`. diff --git a/doc/options.md b/doc/options.md new file mode 100644 index 00000000..959e9291 --- /dev/null +++ b/doc/options.md @@ -0,0 +1,974 @@ +# Configuration options + +There are a number of options you can use, all of them are switched off by +default. +Here is a full list in the same order they are applied while processing css: + +- [always-semicolon](#always-semicolon) +- [remove-empty-rulesets](#remove-empty-rulesets) +- [color-case](#color-case) +- [color-shorthand](#color-shorthand) +- [element-case](#element-case) +- [eof-newline](#eof-newline) +- [leading-zero](#leading-zero) +- [quotes](#quotes) +- [sort-order-fallback](#sort-order-fallback) +- [space-after-colon](#space-after-colon) +- [space-after-combinator](#space-after-combinator) +- [space-after-opening-brace](#space-after-opening-brace) +- [space-after-selector-delimiter](#space-after-selector-delimiter) +- [space-before-colon](#space-before-colon) +- [space-before-combinator](#space-before-combinator) +- [space-before-opening-brace](#space-before-opening-brace) +- [space-before-selector-delimiter](#space-before-selector-delimiter) +- [space-between-declarations](#space-between-declarations) +- [block-indent](#block-indent) +- [sort-order](#sort-order) +- [strip-spaces](#strip-spaces) +- [space-before-closing-brace](#space-before-closing-brace) +- [unitless-zero](#unitless-zero) +- [tab-size](#tab-size) +- [vendor-prefix-align](#vendor-prefix-align) +- [lines-between-rulesets](#lines-between-rulesets) + +Following options are ignored while processing `*.sass` files: + +- always-semicolon +- space-before-opening-brace +- space-after-opening-brace +- space-before-closing-brace +- space-between-declarations + + +## always-semicolon + +Whether to add a semicolon after the *last* value/mixin. + +Acceptable value: `true`. + +Example: `{ "always-semicolon": true }` + +```css +/* before */ +a { color: red; text-decoration: underline } + +/* after */ +a { color: red; text-decoration: underline; } +``` + +### always-semicolon vs. preprocessors + +In `*.scss` and `*.less` files semicolons are not added after `}` +even if it's part of a value. + +Example: `{ "always-semicolon": true }` + +```scss +// before +div { + color: tomato; + font: { + family: fantasy; + size: 16px + } + } + +// after +div { + color: tomato; + font: { + family: fantasy; + size: 16px; + } + } +``` + +## block-indent + +Set indent for code inside blocks, including media queries and nested rules. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces and tabs. Note that line breaks are not + allowed here. + +Example: `{ "block-indent": 4 }` + +```scss +// Before: +a { +top: 0; + p { + color: tomato; +position: happy; + } +} + +// After: +a { + top: 0; + p { + color: tomato; + position: happy; + } + } +``` + +Example: `{ "block-indent": "" }` + +```scss +// Before: +a { +top: 0; + p { + color: tomato; +position: happy; + } +} + +// After: +a { +top: 0; +p { +color: tomato; +position: happy; +} +} +``` + +## color-case + +Unify case of hexadecimal colors. + +Acceptable values: + +* `lower` — for lowercase, +* `upper` — for uppercase. + +Example: `{ "color-case": "lower" }` + +```css +/* before */ +a { color: #FFF } + +/* after */ +a { color: #fff } +``` + +## color-shorthand + +Whether to expand hexadecimal colors or use shorthands. + +Acceptable values: + +* `true` — use shorthands; +* `false` — expand hexadecimal colors to 6 symbols. + +Example: `{ "color-shorthand": true }` + +```css +/* before */ +b { color: #ffcc00 } + +/* after */ +b { color: #fc0 } +``` + +Example: `{ "color-shorthand": false }` + +```css +/* before */ +b { color: #fc0 } + +/* after */ +b { color: #ffcc00 } +``` + +## element-case + +Unify case of element selectors. + +Acceptable values: + +* `lower` — for lowercase; +* `upper` — for uppercase. + +Example: `{ "element-case": "upper" }` + +```css +/* before */ +li > a { color: red } + +/* after */ +LI > A { color: red } +``` + +## eof-newline + +Add/remove line break at EOF. + +Acceptable values: + +* `true` — add line break; +* `false` – remove line break. + +Example: `{ "eof-newline": true }` + +`a { color: red }` → `a { color: red }\n` + +Example: `{ "eof-newline": false }` + +`a { color: red }\n` → `a { color: red }` + +## exclude + +List files that should be ignored while combing. + +Acceptable value: + +* `{String[]}` — array of + [Ant path patterns](http://ant.apache.org/manual/dirtasks.html#patterns). + +Example: `{ "exclude": ["node_modules/**"] }` — exclude all files and +directories under `node_modules` dir. + +## leading-zero + +Add/remove leading zero in dimensions. + +Acceptable values: + +* `true` — add leading zero; +* `false` — remove leading zero. + +Example: `{ "leading-zero": false }` + +```css +/* before */ +p { padding: 0.5em } + +/* after */ +p { padding: .5em } +``` + +## quotes + +Unify quotes style. + +Acceptable values: + +* `single` — transform all `"` to `'`; +* `double` — transform all `'` to `"`. + +Example: `{ "quotes": "single" }` + +```css +/* before */ +p[href^="https://"]:before { content: "secure" } + +/* after */ +p[href^='https://']:before { content: 'secure' } +``` + +## remove-empty-rulesets + +Remove all rulesets that contain nothing but spaces. + +Acceptable value: `true` + +Example: `{ "remove-empty-rulesets": true }`. + +`a { color: red; } p { /* hey */ } b { }` → `a { color: red; } p { /* hey */ } ` + +## sort-order + +Set sort order. + +**Note**: Use one of [predefined +configs](https://github.com/csscomb/csscomb.js/tree/master/config) +as an example. + +Acceptable values: + +* `{Array}` of rules +* `{Array}` of arrays of rules for groups separation + +Example: `{ "sort-order": [ "margin", "padding" ] }` + +```css +/* before */ +p { + padding: 0; + margin: 0; +} + +/* after */ +p { + margin: 0; + padding: 0; +} +``` + +Example: `{ "sort-order": [ [ "margin", "padding" ], [ "border", "background" ] ] }` + +```css +/* before */ +p { + background: none; + border: 0; + margin: 0; + padding: 0; +} + +/* after */ +p { + margin: 0; + padding: 0; + + border: 0; + background: none; +} +``` + +### sort-order vs. preprocessors + +If you sort properties in `*.scss` or `*.less` files, you can use one of 3 +keywords in your config: + +* `$variable` — for variable declarations (e.g. `$var` in Sass or `@var` in LESS); +* `$include` — for all mixins except those that have been specified (e.g. `@include ...` in Sass + or `.mixin()` in LESS); +* `$include mixin-name` — for mixins with specified name (e.g. `@include mixin-name` in Sass + or `.mixin-name()` in LESS); +* `$extend` — for extends (e.g. `@extend .foo` in Sass or `&:extend(.foo)` in LESS); +* `$import` — for `@import` rules. + +Example: `{ "sort-order": [ [ "$variable" ], [ "$include" ], [ "top", "padding" ], [ "$include media" ] ] }` + +```scss +/* before */ +p { + padding: 0; + @include mixin($color); + $color: tomato; + @include media("desktop") { + color: black; + } + top: 0; +} + +/* after */ +p { + $color: tomato; + + @include mixin($color); + + top: 0; + padding: 0; + + @include media("desktop") { + color: black; + } +} +``` + +### sort-order vs. leftovers + +When there are properties that are not mentioned in the `sort-order` option, they are inserted after all the sorted properties in the new group in the same order they were in the source stylesheet. + +You can override this by using a “leftovers” token: `...` — just place it either in its own group, or near other properties in any other group and CSSComb would place all the properties that were not sorted where the `...` was in `sort-order`. + +So, with this value: + +``` json +{ + "sort-order": [ + ["$variable"], + ["position"], + ["...", "border"], + ["$include"], + ["font"] + ] +} +``` + +everything would go into five groups: variables, then group with `position`, then group containing all the leftovers plus the `border`, then group with all includes and then the `font`. + +## sort-order-fallback + +Apply a special sort order for properties that are not specified in `sort-order` +list. +Works great with [leftovers](#sort-order-vs-leftovers). +**Note:** This option is applied only if [sort order](#sort-order) list is +provided. + +Acceptable values: + +* `abc` - sort unknown options alphabetically. + +Example: `{ "sort-order-fallback": "abc", "sort-order": ["top"] }` + +```scss +// Before: +a { + height: 100px; + color: tomato; + top: 0; +} + +// After: +a { + top:0; + + color:tomato; + height: 100px; +} +``` + +Example: `{ "sort-order-fallback": "abc", "sort-order": ["..."] }` + +```scss +// Before: +a { + height: 100px; + color: tomato; + top: 0; +} + +// After: +a { + color:tomato; + height: 100px; + top:0; +} +``` + +## space-after-colon + +Set space after `:` in declarations. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-after-colon": "" }` + +```scss +// Before: +a { + top: 0; + color: tomato; +} + +// After: +a { + top:0; + color:tomato; +} +``` + +Example: `{ "space-after-colon": 1 }` + +```scss +// Before: +a { + top:0; + color:tomato; +} + +// After: +a { + top: 0; + color: tomato; +} +``` + +## space-after-combinator + +Set space after combinator (for example, in selectors like `p > a`). + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-after-combinator": 1 }` + +```scss +// Before: +p>a { color: panda; } + +// After: +p> a { color: panda; } +``` + +Example: `{ "space-after-combinator": "\n " }` + +```scss +// Before: +p > a { color: panda; } + +// After: +p > + a { color: panda; } +``` + +## space-between-declarations + +Set space between declarations (i.e. `color: tomato`). + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-between-declarations": 1 }` + +```scss +// Before: +a { + color: panda; /* comment */ + top: 0; + /* comment */ + right: 0; + position: absolute + } + +// After: +a { + color: panda; /* comment */ top: 0; + /* comment */ + right: 0; position: absolute + } +``` + +Example: `{ "space-between-declarations": "\n " }` + +```scss +// Before: +a { + color: panda; top: 0; right: 0} + +// After: +a { + color: panda; + top: 0; + right: 0;} +``` + + +## space-after-opening-brace + +Set space after `{`. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-after-opening-brace": 1 }` + +```scss +// Before: +a {color: panda;} + +// After: +a { color: panda;} +``` + +Example: `{ "space-after-opening-brace": "\n" }` + +```scss +// Before: +a{color: panda;} + +// After: +a{ +color: panda;} +``` + +## space-after-selector-delimiter + +Set space after selector delimiter. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-after-selector-delimiter": 1 }` + +```scss +// Before: +a,b{ + color: panda; + } + +// After: +a, b { + color: panda; + } +``` + +Example: `{ "space-after-selector-delimiter": "\n" }` + +```scss +// Before: +a, b{ + color: panda; + } + +// After: +a, +b{ + color: panda; + } +``` + +## space-before-closing-brace + +Set space before `}`. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-before-closing-brace": 1 }` + +```scss +// Before: +a { + top: 0; + color: tomato; +} + +// After: +a { + top: 0; + color: tomato; } +``` + +Example: `{ "space-before-closing-brace": "\n" }` + +```scss +// Before: +a { + top: 0; + color: tomato;} + +// After: +a { + top: 0; + color: tomato; +} +``` + +## space-before-colon + +Set space before `:` in declarations. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-before-colon": "" }` + +```scss +// Before: +a { + top : 0; + color : tomato; +} + +// After: +a { + top: 0; + color: tomato; +} +``` + +Example: `{ "space-before-colon": 1 }` + +```scss +// Before: +a { + top:0; + color:tomato; +} + +// After: +a { + top :0; + color :tomato; +} +``` + +## space-before-combinator + +Set space before combinator (for example, in selectors like `p > a`). + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-before-combinator": 1 }` + +```scss +// Before: +p>a { color: panda; } + +// After: +p >a { color: panda; } +``` + +Example: `{ "space-before-combinator": "\n" }` + +```scss +// Before: +p > a { color: panda; } + +// After: +p +> a { color: panda; } +``` + +## space-before-opening-brace + +Set space before `{`. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-before-opening-brace": 1 }` + +```scss +// Before: +a{ + color: panda; + } + +// After: +a { + color: panda; + } +``` + +Example: `{ "space-before-opening-brace": "\n" }` + +```scss +// Before: +a{ + color: panda; + } + +// After: +a +{ + color: panda; + } +``` + +## space-before-selector-delimiter + +Set space before selector delimiter. + +Acceptable values: + +* `{Number}` — number of whitespaces; +* `{String}` — string with whitespaces, tabs or line breaks. + +Example: `{ "space-before-selector-delimiter": 0 }` + +```scss +// Before: +a , b{ + color: panda; + } + +// After: +a, b { + color: panda; + } +``` + +Example: `{ "space-before-selector-delimiter": "\n" }` + +```scss +// Before: +a, b{ + color: panda; + } + +// After: +a +,b{ + color: panda; + } +``` + +## strip-spaces + +Whether to trim trailing spaces. + +Acceptable value: `true`. + +Example: `{ "strip-spaces": true }` + +`a { color: red } \n \n \n` → `a { color: red }\n` + +`a { color: red }\t` → `a { color: red }` + +## tab-size + +Set tab size (number of spaces to replace hard tabs). + +Acceptable values: + +* `{Number}` — number of whitespaces; + +Example: `{ "tab-size": 2 }` + +```scss +// Before: +a{ + color: panda; + } + +// After: +a { + color: panda; + } +``` + +## template + +**Note:** See [configuration docs](configuration.md#override-templates-settings) +for more information. + +Acceptable value: + +* `{String}` — path to the `.css` file. + +Example: `{ "template": "example.css" }` + +## unitless-zero + +Whether to remove units in zero-valued dimensions. + +Acceptable value: `true`. + +Example: `{ "unitless-zero": true }` + +```css +/* before */ +img { border: 0px } + +/* after */ +img { border: 0 } +``` + +## vendor-prefix-align + +Whether to align prefixes in properties and values. + +Acceptable value: `true`. + +Example: `{ "vendor-prefix-align": true }` + +```css +/* before */ +a +{ + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + background: -webkit-linear-gradient(top, #fff 0, #eee 100%); + background: -moz-linear-gradient(top, #fff 0, #eee 100%); + background: linear-gradient(to bottom, #fff 0, #eee 100%); +} + +/* after */ +a +{ + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; + background: -webkit-linear-gradient(top, #fff 0, #eee 100%); + background: -moz-linear-gradient(top, #fff 0, #eee 100%); + background: linear-gradient(to bottom, #fff 0, #eee 100%); +} +``` + +## lines-between-rulesets + +Number of line breaks between rulesets or @rules. + +Acceptable values: + +* `{Number}` — number of newlines; + +Example: `{ "lines-between-rulesets": 1}` + +```scss +// Before: +.foo { + @include border-radius(5px); + background: red; + .baz { + .test { + height: 50px; + } + } +}.bar { + border: 1px solid red; + @media (min-width: 500px) { + width: 50px; + } +} + +// After: +.foo { + @include border-radius(5px); + background: red; + + .baz { + .test { + height: 50px; + } + } +} + +.bar { + border: 1px solid red; + + @media (min-width: 500px) { + width: 50px; + } +} +``` + +## verbose + +Whether to use `--verbose` option in CLI. + +Acceptable value: `true`. + +Example: `{ "verbose": true }` + +```bash +csscomb ./test + +✓ test/integral.origin.css + test/integral.expect.css + +2 files processed +1 file fixed +96 ms spent +``` diff --git a/doc/usage-cli.md b/doc/usage-cli.md new file mode 100644 index 00000000..1f0a37fa --- /dev/null +++ b/doc/usage-cli.md @@ -0,0 +1,102 @@ +# Command Line usage + +To run `csscomb`: + +```bash +csscomb path[ path[...]] +``` + +`path` can be either a directory or a file: + +```bash +csscomb assets/css public/styles.css +``` + +If you installed the package locally, use local bin file instead: + +```bash +./node_modules/.bin/csscomb assets/css public/styles.css +``` + +## Options + +### help + +If you run `csscomb -h`, it will show some helpful information: + +```bash +csscomb -h + + Usage: csscomb [options] + + Options: + + -h, --help output usage information + -V, --version output the version number + -v, --verbose verbose mode + -c, --config [path] configuration file path + -d, --detect detect mode (would return detected options) + -l, --lint in case some fixes needed returns an error + -t, --tty-mode execution in TTY mode (useful, when running tool using external app, e.g. IDE) +``` + +### config + +If you want to use custom config instead of predefined `csscomb.json` just +put a file named `.csscomb.json` to project's root (see [configuration +docs](configuration.md#where-to-put-config) for more information). +However, if for some reason you would like to use custom path, do it this way: + +```bash +csscomb -c path/to/config styles.css +``` + +### detect + +If you want to generate a config file based on a template file, run: + +```bash +csscomb -d example.css > .csscomb.json +``` + +See [configuration docs](configuration.md#generate-config-from-a-template) for +more information. + +### lint + +CSScomb can be used as a linter, i.e. telling you what should be changed instead +of modifying anything. +This option should be combined with `--verbose`: + +```bash +csscomb -lv assets/css + + assets/css/main.scss +! assets/css/main.scss +! assets/css/main.scss +! assets/css/main.scss + +4 files processed +0 files fixed +spent: 30ms +``` + +Exclamation mark is a sign that something is wrong with the file. + +### verbose + +If you are curious or you just like a lot of output info, `--verbose` is your +good friend: + +```bash +csscomb -v assets/css + + assets/css/main.scss +✓ assets/css/main.scss +✓ assets/css/main.scss +✓ assets/css/main.scss + +4 files processed +3 files fixed +spent: 33ms +``` diff --git a/doc/usage-node.md b/doc/usage-node.md new file mode 100644 index 00000000..5a3fbcc6 --- /dev/null +++ b/doc/usage-node.md @@ -0,0 +1,113 @@ +# Node.js module usage + +CSScomb can be used in Node.js projects: inside a plugin or as a dev tool. + +Workflow can look like this: + +```js +// Require: +var Comb = require('csscomb'); +var config = require('path/to/config'); + +// Configure: +var comb = new Comb(config); + +// Use: +comb.processPath('style.css'); +``` + +## new Comb(config) + +Create instance's prototype. + +Parameters: + +* `{String|Object} config` — config that should be used after creating + instance. Should be JSON or one of predefined config's name. Optional. + +Example: Create CSScomb instance and configure it using predefined `yandex` sort +order + +```js +var comb = new Comb('yandex'); + +// This is shortcut for: +var comb = new Comb(); +var config = Comb.getConfig('yandex'); +comb.configure(config); +``` + +Example: Create CSScomb instance and configure it using config object + +```js +var config = require('path/to/config'); +var comb = new Comb(config); + +// This is shortcut for: +var comb = new Comb(); +comb.configure(config); +``` + +## Comb.getConfig(name) + +Get one of predefined configs. + +Note that this is a static method. + +Parameters: + +* `{String} name` — config's name. Should be one of the following: + `csscomb`, `zen` or `yandex`. + +Example: Configure CSScomb using predefined `zen` sort order that is slightly +modified. + +```js +var config = Comb.getConfig('zen'); +config['always-semicolon'] = true; +comb.configure(config); +``` + +## Comb.detectInFile(path, options) + +Get config options that can be detected in a file. + +Note that this is a static method. + +Parameters: + +* `{String} path` — path to stylesheet +* `{Array} options` — list of options to detect. Optional. By default tries + to detect all available options. + +Example: Configure CSScomb using template file + +```js +var config = comb.detectInFile('template.css'); +comb.configure(config); +``` + +## Comb.detectInString(string, options) + +Get config options that can be detected in a string. + +Note that this is a static method. + +Parameters: + +* `{String} string` — stylesheet +* `{Array} options` — list of options to detect. Optional. By default tries + to detect all available options. + +Example: Configure CSScomb using template stylesheet + +```js +var css = 'a {top: 0; left: 0}'; +var config = comb.detectInString(css); +comb.configure(config); +``` + +## Public methods + +For the list of public methods and examples of their usage, see [CSScomb Core docs](https://github.com/csscomb/core). + diff --git a/lib/cli.js b/lib/cli.js deleted file mode 100644 index 26e5461c..00000000 --- a/lib/cli.js +++ /dev/null @@ -1,116 +0,0 @@ -/** - * Command line implementation for CSSComb - * - * Usage example: - * ./node_modules/.bin/csscomb [options] file1 [dir1 [fileN [dirN]]] - */ -var fs = require('fs'); -var path = require('path'); -var program = require('commander'); -var vow = require('vow'); -var Comb = require('./csscomb'); - -program - .version(require('../package.json').version) - .usage('[options] ') - .option('-v, --verbose', 'verbose mode') - .option('-c, --config [path]', 'configuration file path') - .option('-d, --detect', 'detect mode (would return detected options)') - .option('-l, --lint', 'in case some fixes needed returns an error') - .parse(process.argv); - -if (!program.args.length) { - console.log('No input paths specified'); - program.help(); -} - -/** - * Look for a config file: recursively from current (process) directory - * up to $HOME dir - * @param {String} configPath - * @returns {String} - */ -function getConfigPath(configPath) { - var HOME = process.env.HOME || process.env.HOMEPATH || process.env.USERPROFILE; - // Since `process.cwd()` can be absolutely anything, build default path - // relative to current directory: - var defaultConfigPath = path.join(__dirname, '../config/csscomb.json'); - - configPath = configPath || path.join(process.cwd(), '.csscomb.json'); - - // If we've finally found a config, return its path: - if (fs.existsSync(configPath)) return configPath; - - // If we are in HOME dir already and yet no config file, return a default - // one from our package. - // If project is located not under HOME, compare to root instead. - // Since there appears to be no good way to get root path in - // Windows, assume that if current dir has no parent dir, we're in - // root. - var dirname = path.dirname(configPath); - var parentDirname = path.dirname(dirname); - if (dirname === HOME || dirname === parentDirname) return defaultConfigPath; - - // If there is no config in this directory, go one level up and look for - // a config there: - configPath = path.join(parentDirname, '.csscomb.json'); - return getConfigPath(configPath); -} - -var configPath = program.config || getConfigPath(); -var comb = new Comb(); - -if (program.detect) { - console.log(JSON.stringify(comb.detectInFile(program.args[0]), false, 4)); - process.exit(0); -} - -if (fs.existsSync(configPath)) { - var config; - if (configPath.match(/\.css$/)) { - config = comb.detectInFile(configPath); - } else { - config = require(configPath); - } - - if (config.template) { - if (fs.existsSync(config.template)) { - var templateConfig = comb.detectInFile(config.template); - for (var attrname in templateConfig) { - if (!config[attrname]) { - config[attrname] = templateConfig[attrname]; - } - } - } else { - console.log('Template configuration file ' + config.template + ' was not found.'); - process.exit(1); - } - } - - console.time('spent'); - - config.verbose = program.verbose === true || config.verbose; - config.lint = program.lint; - - comb.configure(config); - - vow.all(program.args.map(comb.processPath.bind(comb))) - .then(function() { - if (config.verbose) { - console.log(''); - console.log(comb.processed + ' file' + (comb.processed === 1 ? '' : 's') + ' processed'); - console.log(comb.changed + ' file' + (comb.changed === 1 ? '' : 's') + ' fixed'); - console.timeEnd('spent'); - } - if (config.lint && comb.tbchanged) { - process.exit(1); - } - }) - .fail(function(e) { - console.log('stack: ', e.stack); - process.exit(1); - }); -} else { - console.log('Configuration file ' + configPath + ' was not found.'); - process.exit(1); -} diff --git a/lib/csscomb.js b/lib/csscomb.js deleted file mode 100644 index 37d75765..00000000 --- a/lib/csscomb.js +++ /dev/null @@ -1,389 +0,0 @@ -var gonzales = require('gonzales-pe'); -var minimatch = require('minimatch'); -var vow = require('vow'); -var fs = require('fs'); -var vfs = require('vow-fs'); -var doNothing = function() {}; - -/** - * Starts Code Style processing process. - * - * @constructor - * @name Comb - */ -var Comb = function(config) { - this.SUPPORTED_SYNTAXES = ['css', 'scss', 'less']; - this._options = [ - 'remove-empty-rulesets', - 'always-semicolon', - 'color-case', - 'color-shorthand', - 'element-case', - 'leading-zero', - 'quotes', - 'strip-spaces', - 'eof-newline', - 'stick-brace', - 'colon-space', - 'combinator-space', - 'rule-indent', - 'block-indent', - 'unitless-zero', - 'sort-order', - 'vendor-prefix-align' - ]; - this._exclude = null; - this._detect = false; - - // If config was passed, configure: - if (typeof config === 'string' && - ['csscomb', 'zen', 'yandex'].indexOf(config) > -1) { - config = require('../config/' + config + '.json'); - } - - if (typeof config === 'object') this.configure(config); - - // Return Comb's object to make creating new instance chainable: - return this; -}; - -Comb.prototype = { - - /** - * Get one of configuration files from `config` directory - * @param {String} name Config's name: 'csscomb', 'zen' or 'yandex' - * @returns {Object} Configuration object - */ - getConfig: function(name) { - name = name || 'csscomb'; - - if (typeof name !== 'string') { - throw new Error('Config name should be a string'); - } - - if (['csscomb', 'zen', 'yandex'].indexOf(name) < 0) { - var message = name + ' is not a valid config name. Try one of ' + - 'the following: \'csscomb\', \'zen\' or \'yandex\'.'; - throw new Error(message); - } - - return require('../config/' + name + '.json'); - }, - - /** - * Loads configuration from JSON. - * Activates and configures required options. - * - * @param {Object} config - */ - configure: function(config) { - this._handlers = []; - this._options.forEach(function(option) { - if (typeof config[option] === 'undefined') return; - - try { - var handler = require('./options/' + option).setValue(config[option]); - handler && this._handlers.push(handler); - } catch (e) { - console.warn('Error loading "%s" handler: %s', option, e.message); - } - }, this); - - this._exclude = (config.exclude || []).map(function(pattern) { - return new minimatch.Minimatch(pattern); - }); - - this.processed = 0; - this.tbchanged = 0; - this.changed = 0; - this._verbose = config.verbose; - this._lint = config.lint; - - // Return Comb's object to make the method chainable: - return this; - }, - - /** - * Detects the options in the given string - * - * @param {String} text Stylesheet - * @param {Array} options List of options to detect - * @returns {Object} - */ - detectInString: function(text, options) { - var result; - this._detect = true; - this._detected = {}; - this._handlers = []; - this._options.forEach(function(option) { - try { - var handler = require('./options/' + option); - if (!handler || options && options.indexOf(option) === -1) return; - - handler._name = option; - this._detected[option] = []; - this._handlers.push(handler); - } catch (e) { - console.warn('Error loading "%s" handler: %s', option, e.message); - } - }, this); - - result = this.processString(text); - this._detect = false; - return result; - }, - - /** - * Detects the options in the given file - * - * @param {String} path Path to the stylesheet - * @param {Array} options List of options to detect - * @returns {Object} - */ - detectInFile: function(path, options) { - var stylesheet = fs.readFileSync(path, 'utf8'); - return this.detectInString(stylesheet, options); - }, - - /** - * Processes stylesheet tree node. - * - * @param {Array} tree Parsed tree - * @returns {Array} - */ - processTree: function(tree) { - // We walk across complete tree for each handler, - // because we need strictly maintain order in which handlers work, - // despite fact that handlers work on different level of the tree. - this._handlers.forEach(function(handler) { - this.processNode(['tree', tree], 0, handler); - }, this); - return tree; - }, - - /** - * Processes tree node. - * @param {Array} node Tree node - * @param {Number} level Indent level - */ - processNode: function(node, level, handler) { - node.forEach(function(node) { - if (!Array.isArray(node)) return; - - var nodeType = node.shift(); - if (this._detect) { - if (handler.detect) { - var detected = handler.detect(nodeType, node, level); - if (detected !== undefined) { - this._detected[handler._name].push(detected); - } - } - } else { - handler.process(nodeType, node, level); - } - node.unshift(nodeType); - - if (nodeType === 'atrulers') level++; - - this.processNode(node, level, handler); - }, this); - }, - - /** - * Process file provided with a string. - * @param {String} text - * @param {String} [syntax] Syntax name (e.g. `scss`) - * @param {String} [filename] - */ - processString: function(text, syntax, filename) { - if (!text) return text; - var tree; - var string = JSON.stringify; - try { - tree = gonzales.cssToAST({ syntax: syntax, css: text }); - } catch (e) { - throw new Error('Parsing error at ' + filename + ': ' + e.message); - } - if (typeof tree === 'undefined') { - throw new Error('Undefined tree at ' + filename + ': ' + string(text) + ' => ' + string(tree)); - } - tree = this.processTree(tree); - if (this._detect) { - return this._getDetectedOptions(this._detected); - } else { - return gonzales.astToCSS({ syntax: syntax, ast: tree }); - } - }, - - /** - * Process single file. - * - * @param {String} path - * @returns {Promise} - */ - processFile: function(path) { - var _this = this; - if (this._shouldProcessFile(path)) { - return vfs.read(path, 'utf8').then(function(data) { - var syntax = path.split('.').pop(); - var processedData = _this.processString(data, syntax, path); - var changed = data !== processedData; - var lint = _this._lint; - - var tick = changed ? (lint ? '!' : '✓') : ' '; - var message = _this._verbose ? console.log.bind(null, tick, path) : doNothing; - - _this.processed++; - changed && _this.tbchanged++; - - if (!changed || lint) { - message(); - } else { - return vfs.write(path, processedData, 'utf8').then(function() { - _this.changed++; - message(); - }); - } - }); - } - return null; - }, - - /** - * Process directory recursively. - * - * @param {String} path - * @returns {Promise} - */ - processDirectory: function(path) { - var _this = this; - return vfs.listDir(path).then(function(filenames) { - return vow.all(filenames.map(function(filename) { - var fullname = path + '/' + filename; - return vfs.stat(fullname).then(function(stat) { - if (stat.isDirectory()) { - return _this._shouldProcess(fullname) && _this.processDirectory(fullname); - } else { - return vow.when(_this.processFile(fullname)).then(function(errors) { - if (errors) return errors; - }); - } - }); - })).then(function(results) { - return [].concat.apply([], results); - }); - }); - }, - - /** - * Process directory or file. - * - * @param {String} path - */ - processPath: function(path) { - path = path.replace(/\/$/, ''); - var _this = this; - return vfs.exists(path).then(function(exists) { - if (exists) { - return vfs.stat(path).then(function(stat) { - if (stat.isDirectory()) { - return _this.processDirectory(path); - } else { - return vow.when(_this.processFile(path)).then(function(errors) { - if (errors) return [errors]; - return; - }); - } - }); - } else { - throw new Error('Path ' + path + ' was not found.'); - } - }); - }, - - /** - * Returns true if specified path is not in excluded list. - * - * @returns {Boolean} - */ - _shouldProcess: function(path) { - path = path.replace(/^\.\//, ''); - var exclude = this._exclude; - for (var i = exclude.length; i--;) { - if (exclude[i].match(path)) return false; - } - return true; - }, - - /** - * Returns true if specified path is not in excluded list and has one of - * acceptable extensions. - * - * @returns {Boolean} - */ - _shouldProcessFile: function(path) { - // Get file's extension: - var syntax = path.split('.').pop(); - // Check if syntax is supported. If not, ignore the file: - if (this.SUPPORTED_SYNTAXES.indexOf(syntax) < 0) { - return false; - } - - return this._shouldProcess(path); - }, - - /** - * Gets the detected options. - * - * @param {Object} detected - * @returns {Object} - */ - _getDetectedOptions: function(detected) { - var options = {}; - Object.keys(detected).forEach(function(option) { - // List of all the detected variants from the stylesheet for the given option: - var values = detected[option]; - var i; - if (values.length) { - if (values.length === 1) { - options[option] = values[0]; - } else { - // If there are more than one value for the option, find the most popular one; - // `variants` would be populated with the popularity for different values. - var variants = {}; - var bestGuess = null; - var maximum = 0; - for (i = values.length; i--;) { - var currentValue = values[i]; - // Count the current value: - if (variants[currentValue]) { - variants[currentValue]++; - } else { - variants[currentValue] = 1; - } - // If the current variant is the most popular one, treat it as the best guess: - if (variants[currentValue] >= maximum) { - maximum = variants[currentValue]; - bestGuess = currentValue; - } - } - if (bestGuess !== null) { - options[option] = bestGuess; - } - } - } else { - // If there are no values for the option, check if there is a default one: - for (i = this._handlers.length; i--;) { - if (this._handlers[i]._name === option && this._handlers[i]._detectDefault !== undefined) { - options[option] = this._handlers[i]._detectDefault; - break; - } - } - } - }, this); - - return options; - } -}; - -module.exports = Comb; diff --git a/lib/options/always-semicolon.js b/lib/options/always-semicolon.js deleted file mode 100644 index 7a0d32a3..00000000 --- a/lib/options/always-semicolon.js +++ /dev/null @@ -1,91 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {String|Boolean} value Option value - * @returns {Object|undefined} - * TODO: This option accepts only boolean - */ - setValue: function(value) { - this._value = value === true; - if (!this._value) return; - return this; - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'block') { - for (var i = node.length; i--;) { - var currentNode = node[i]; - var currentNodeType = currentNode[0]; - var nodeWithoutSemicolon; - - // Skip nodes that already have `;` at the end: - if (currentNodeType === 'declDelim') break; - - // Add semicolon only after declarations and includes. - // If current node is include, insert semicolon right into it. - // If it's declaration, look for value node: - if (currentNodeType === 'include') { - nodeWithoutSemicolon = currentNode; - } else if (currentNodeType === 'declaration') { - for (var k = currentNode.length; k--;) { - if (currentNode[k][0] === 'value') { - nodeWithoutSemicolon = currentNode[k]; - break; - } - } - } else { - continue; - } - - var space = []; - var isBlock = false; - - // Check if there are spaces and comments at the end of the node: - for (var j = nodeWithoutSemicolon.length; j--;) { - var lastNode = nodeWithoutSemicolon[j][0]; - // If the node's last child is block, do not add semicolon: - // TODO: Add syntax check and run the code only for scss - if (lastNode === 'block') { - isBlock = true; - break; - } else if (['s', 'commentML', 'commentSL'].indexOf(lastNode) === -1) break; - - space.unshift(nodeWithoutSemicolon[j]); - } - - if (isBlock) break; - - // Temporarily remove last spaces and comments and insert `;` - // before them: - nodeWithoutSemicolon.splice(nodeWithoutSemicolon.length - space.length); - node.splice.apply(node, [i + 1, 0, ['declDelim']].concat(space)); - break; - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - if (nodeType === 'block') { - for (var i = node.length; i--;) { - var nodeItem = node[i]; - var type = nodeItem[0]; - if (type === 'declDelim') return true; - - if (type === 'declaration') return false; - } - } - } -}; diff --git a/lib/options/block-indent.js b/lib/options/block-indent.js deleted file mode 100644 index 4c46e278..00000000 --- a/lib/options/block-indent.js +++ /dev/null @@ -1,103 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {String|Number} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - delete this._value; - - if (typeof value === 'number' && value === Math.abs(Math.round(value))) { - this._value = new Array(value + 1).join(' '); - } else if (typeof value === 'string' && value.match(/^[ \t]+$/)) { - this._value = value; - } - - if (typeof this._value === 'string') return this; - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node, level) { - var value; - var space; - - // Add right space before closing brace - if (nodeType === 'atrulers' || nodeType === 'block') { - value = '\n' + new Array(level + 1).join(this._value); - if (node[node.length - 1][0] === 's') node.pop(); - node.push(['s', value]); - } - - // Add right space before rule declaration - if (nodeType === 'stylesheet' || nodeType === 'atrulers') { - // Level up hack - if (nodeType === 'atrulers') level++; - - // Prevent line break at file start - var isFirst; - if (nodeType === 'stylesheet') { - var first = node[0]; - if (first[0] !== 's' || first[1].match('\n') === null) isFirst = true; - } - - for (var i = 0; i < node.length; i++) { - var nodeItem = node[i]; - if (nodeItem[0] === 'atruler' || nodeItem[0] === 'ruleset') { - value = (i < 2 && isFirst ? '' : '\n') + new Array(level + 1).join(this._value); - isFirst = false; - - space = node[i - 1]; - if (!space || space[0] !== 's') { - var tail = node.splice(i); - space = ['s', '']; - tail.unshift(space); - Array.prototype.push.apply(node, tail); - i++; - } - space[1] = space[1].replace(/(\n)?([\t ]+)?$/, value); - } - } - } - - // Add space before block opening brace - if (nodeType === 'simpleselector') { - space = node[node.length - 1]; - if (space[0] === 's' && space[1].match('\n')) { - space[1] += new Array(level + 1).join(this._value); - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node, level) { - var result = null; - if (nodeType === 'atrulers' || nodeType === 'block') { - if (node.length && node[node.length - 1][0] === 's' && level > 0) { - result = node[node.length - 1][1].replace(/\s*\n/g, ''); - - if (this._prev !== undefined && this._prev[0] < level) { - result = result.replace(result.replace(this._prev[1], ''), ''); - } - if (this._prev === undefined || this._prev[0] !== level) { - this._prev = [level, result]; - } - } - } - - if (result !== null) { - return result; - } - } - -}; diff --git a/lib/options/colon-space.js b/lib/options/colon-space.js deleted file mode 100644 index edba4262..00000000 --- a/lib/options/colon-space.js +++ /dev/null @@ -1,80 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {Array} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - delete this._value; - - if (value.constructor !== Array) return; - - if (typeof value[0] === 'number' && - value[0] === Math.abs(Math.round(value[0]))) { - value[0] = new Array(value[0] + 1).join(' '); - } else if (typeof value[0] !== 'string' || - !value[0].match(/^[ \t\n]*$/)) { - return; - } - - if (typeof value[1] === 'number' && - value[1] === Math.abs(Math.round(value[1]))) { - value[1] = new Array(value[1] + 1).join(' '); - } else if (typeof value[1] !== 'string' || - !value[1].match(/^[ \t\n]*$/)) { - return; - } - - this._value = value; - return this; - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'property') { - if (node[node.length - 1][0] === 's') node.pop(); - if (this._value[0] !== '') node.push(['s', this._value[0]]); - } - if (nodeType === 'value') { - if (node[0][0] === 's') node.shift(); - if (this._value[1] !== '') node.unshift(['s', this._value[1]]); - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - var result = []; - - if (nodeType === 'declaration') { - var property = node[0]; - var value = node[2]; - - if (property[property.length - 1][0] === 's') { - result[0] = property[property.length - 1][1]; - } else { - result[0] = ''; - } - - if (value[1][0] === 's') { - result[1] = value[1][1]; - } else { - result[1] = ''; - } - - } - if (result.length) { - return result; - } - } -}; diff --git a/lib/options/color-case.js b/lib/options/color-case.js deleted file mode 100644 index d88212b3..00000000 --- a/lib/options/color-case.js +++ /dev/null @@ -1,46 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {String} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - if (value === 'lower' || value === 'upper') { - this._value = value; - return this; - } - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'vhash') { - if (this._value === 'lower') { - node[0] = node[0].toLowerCase(); - } else if (this._value === 'upper') { - node[0] = node[0].toUpperCase(); - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - if (nodeType === 'vhash') { - if (node[0].match(/^[^A-F]*[a-f][^A-F]*$/)) { - return 'lower'; - } else if (node[0].match(/^[^a-f]*[A-F][^a-f]*$/)) { - return 'upper'; - } - } - } -}; diff --git a/lib/options/color-shorthand.js b/lib/options/color-shorthand.js deleted file mode 100644 index 39a2dcff..00000000 --- a/lib/options/color-shorthand.js +++ /dev/null @@ -1,46 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {Boolean} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - if (value === true || value === false) { - this._value = value; - return this; - } - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'vhash') { - if (this._value) { - node[0] = node[0].replace(/(\w)\1(\w)\2(\w)\3/i, '$1$2$3'); - } else { - node[0] = node[0].replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3'); - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - if (nodeType === 'vhash') { - if (node[0].match(/^\w{3}$/)) { - return true; - } else if (node[0].match(/^(\w)\1(\w)\2(\w)\3$/)) { - return false; - } - } - } -}; diff --git a/lib/options/combinator-space.js b/lib/options/combinator-space.js deleted file mode 100644 index 78dbdcb6..00000000 --- a/lib/options/combinator-space.js +++ /dev/null @@ -1,112 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {Array} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - delete this._value; - - if (value.constructor !== Array) return; - - if (typeof value[0] === 'number' && - value[0] === Math.abs(Math.round(value[0]))) { - value[0] = new Array(value[0] + 1).join(' '); - } else if (typeof value[0] !== 'string' || - !value[0].match(/^[ \t\n]*$/)) { - return; - } - - if (typeof value[1] === 'number' && - value[1] === Math.abs(Math.round(value[1]))) { - value[1] = new Array(value[1] + 1).join(' '); - } else if (typeof value[1] !== 'string' || - !value[1].match(/^[ \t\n]*$/)) { - return; - } - - this._value = value; - return this; - - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'selector') { - for (var i = node.length; i--;) { - var subSelector = node[i]; - for (var j = subSelector.length; j--;) { - if (subSelector[j][0] === 'combinator') { - // Working with the whitespace after the combinator - if (subSelector[j + 1][0] === 's') { - subSelector[j + 1][1] = this._value[1]; - } else { - subSelector.splice(j + 1, 0, ['s', this._value[1]]); - } - // Working with the whitespace before the combinator - if (subSelector[j - 1][0] === 's') { - subSelector[j - 1][1] = this._value[0]; - } else { - subSelector.splice(j, 0, ['s', this._value[0]]); - } - } - } - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - if (nodeType === 'selector') { - var variants = {}; - var bestGuess = null; - var maximum = 0; - for (var i = node.length; i--;) { - var subSelector = node[i]; - for (var j = subSelector.length; j--;) { - var result = []; - if (subSelector[j][0] === 'combinator') { - // Working with the whitespace after the combinator - if (subSelector[j + 1][0] === 's') { - result[1] = subSelector[j + 1][1]; - } else { - result[1] = ''; - } - // Working with the whitespace before the combinator - if (subSelector[j - 1][0] === 's') { - result[0] = subSelector[j - 1][1]; - } else { - result[0] = ''; - } - } - - if (result.length) { - if (variants[result]) { - variants[result]++; - } else { - variants[result] = 1; - } - if (variants[result] > maximum) { - maximum = variants[result]; - bestGuess = result; - } - } - } - } - if (bestGuess) { - return bestGuess; - } - } - } -}; diff --git a/lib/options/element-case.js b/lib/options/element-case.js deleted file mode 100644 index 832a576b..00000000 --- a/lib/options/element-case.js +++ /dev/null @@ -1,75 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {String} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - if (value === 'lower' || value === 'upper') { - this._value = value; - return this; - } - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'simpleselector') { - for (var i = node.length; i--;) { - var nodeItem = node[i]; - if (nodeItem[0] === 'ident') { - if (this._value === 'lower') { - nodeItem[1] = nodeItem[1].toLowerCase(); - } else if (this._value === 'upper') { - nodeItem[1] = nodeItem[1].toUpperCase(); - } - } - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - if (nodeType === 'simpleselector') { - var variants = {}; - var bestGuess = null; - var maximum = 0; - for (var i = node.length; i--;) { - var nodeItem = node[i]; - if (nodeItem[0] === 'ident') { - var result; - if (nodeItem[1].match(/^[a-z]+$/)) { - result = 'lower'; - } else if (nodeItem[1].match(/^[A-Z]+$/)) { - result = 'upper'; - } - - if (result) { - if (variants[result]) { - variants[result]++; - } else { - variants[result] = 1; - } - if (variants[result] > maximum) { - maximum = variants[result]; - bestGuess = result; - } - } - } - } - if (bestGuess) { - return bestGuess; - } - } - } -}; diff --git a/lib/options/eof-newline.js b/lib/options/eof-newline.js deleted file mode 100644 index 9208b6a4..00000000 --- a/lib/options/eof-newline.js +++ /dev/null @@ -1,48 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {Boolean} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - if (value === true || value === false) { - this._value = value; - return this; - } - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'stylesheet') { - var lastChild = node[node.length - 1]; - if (lastChild[0] !== 's') { - lastChild = ['s', '']; - node.push(lastChild); - } - lastChild[1] = lastChild[1].replace(/\n$/, ''); - if (this._value) lastChild[1] += '\n'; - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - if (nodeType === 'stylesheet') { - if (node[node.length - 1][0] === 's' && node[node.length - 1][1].indexOf('\n') !== -1) { - return true; - } else { - return false; - } - } - } -}; diff --git a/lib/options/leading-zero.js b/lib/options/leading-zero.js deleted file mode 100644 index cff306bd..00000000 --- a/lib/options/leading-zero.js +++ /dev/null @@ -1,47 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {Boolean} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - if (value === true || value === false) { - this._value = value; - return this; - } - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'number') { - if (this._value) { - if (node[0][0] === '.') - node[0] = '0' + node[0]; - } else { - node[0] = node[0].replace(/^0+(?=\.)/, ''); - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - if (nodeType === 'number') { - if (node.toString().match(/^\.[0-9]+/)) { - return false; - } else if (node.toString().match(/^0\.[0-9]+/)) { - return true; - } - } - } -}; diff --git a/lib/options/quotes.js b/lib/options/quotes.js deleted file mode 100644 index a2c93351..00000000 --- a/lib/options/quotes.js +++ /dev/null @@ -1,57 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {String|Boolean} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - delete this._value; - - if (value === 'single' || value === 'double' ) { - this._value = value; - } - - if (!this._value) return; - return this; - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'string') { - if (node[0][0] === '"' && this._value === 'single') { - node[0] = node[0] - .replace(/\\"/g, '"') // unescape all escaped double quotes - .replace(/([^\\])'/g, '$1\\\'') // escape all the single quotes - .replace(/^"|"$/g, '\''); // replace the first and the last quote - - } else if (node[0][0] === '\'' && this._value === 'double') { - node[0] = node[0] - .replace(/\\'/g, '\'') // unescape all escaped single quotes - .replace(/([^\\])"/g, '$1\\\"') // escape all the double quotes - .replace(/^'|'$/g, '"'); // replace the first and the last quote - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - if (nodeType === 'string') { - if (node[0][0] === '"') { - return 'double'; - } else if (node[0][0] === '\'') { - return 'single'; - } - } - } -}; diff --git a/lib/options/remove-empty-rulesets.js b/lib/options/remove-empty-rulesets.js deleted file mode 100644 index e2e3ed06..00000000 --- a/lib/options/remove-empty-rulesets.js +++ /dev/null @@ -1,123 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {String} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - if (value === true) { - this._value = value; - return this; - } - }, - - /** - * Remove rulesets with no declarations. - * - * @param {String} nodeType - * @param {Array} nodeContent - */ - process: function(nodeType, nodeContent) { - if (nodeType === 'stylesheet') { - this._processStylesheetContent(nodeContent); - } - }, - - _processStylesheetContent: function(nodeContent) { - this._removeEmptyRulesets(nodeContent); - this._mergeAdjacentWhitespace(nodeContent); - }, - - _removeEmptyRulesets: function(nodeContent) { - var i = nodeContent.length; - // Loop through node and try to find a ruleset: - while (i--) { - var node = nodeContent[i]; - if (!this._isRuleset(node)) continue; - // If a ruleset is found, try to find its nested rulesets and remove - // all empty ones: - var j = node.length; - while (j--) { - // Nested rulesets are located inside blocks, that's why look - // for blocks only: - var blockNode = node[j]; - if (blockNode[0] !== 'block') continue; - blockNode.shift(); - this._processStylesheetContent(blockNode); - blockNode.unshift('block'); - node[j] = blockNode; - } - // If after removing all empty nested rulesets the parent has also - // become empty, remove it too: - if (this._isEmptyRuleset(node)) { - nodeContent.splice(i, 1); - } - } - }, - - /** - * Removing ruleset nodes from tree may result in two adjacent whitespace nodes which is not correct AST: - * [space, ruleset, space] => [space, space] - * To ensure correctness of further processing we should merge such nodes into one. - * [space, space] => [space] - */ - _mergeAdjacentWhitespace: function(nodeContent) { - var i = nodeContent.length - 1; - while (i-- > 0) { - if (this._isWhitespace(nodeContent[i]) && this._isWhitespace(nodeContent[i + 1])) { - nodeContent[i][1] += nodeContent[i + 1][1]; - nodeContent.splice(i + 1, 1); - } - } - }, - - _isEmptyRuleset: function(ruleset) { - return ruleset.filter(this._isBlock).every(this._isEmptyBlock, this); - }, - - /** - * Block is considered empty when it has nothing but spaces. - */ - _isEmptyBlock: function(node) { - return node.length === 1 || !node.some(this._isNotWhitespace); - }, - - _isDeclarationOrComment: function(node) { - return ['declaration', 'commentML', 'commentSL'].indexOf(node[0]) > -1; - }, - - _isRuleset: function(node) { - return node[0] === 'ruleset'; - }, - - _isBlock: function(node) { - return node[0] === 'block'; - }, - - _isWhitespace: function(node) { - return node[0] === 's'; - }, - - _isNotWhitespace: function(node) { - return typeof node === 'object' && node[0] !== 's'; - }, - - /** - * Detects the value of an option at the tree node. - * This option is treated as `true` by default, but any trailing space would invalidate it. - * - * @param {String} nodeType - * @param {node} node - */ - _detectDefault: true, - - detect: function(nodeType, node) { - if (nodeType === 'atrulers' || nodeType === 'block') { - if (node.length === 0 || (node.length === 1 && node[0][0] === 's')) { - return false; - } - } - } -}; diff --git a/lib/options/rule-indent.js b/lib/options/rule-indent.js deleted file mode 100644 index 18cd8edb..00000000 --- a/lib/options/rule-indent.js +++ /dev/null @@ -1,83 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {String|Number} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - delete this._value; - - if (typeof value === 'number' && value === Math.abs(Math.round(value))) { - this._value = new Array(value + 1).join(' '); - } else if (typeof value === 'string' && value.match(/^[ \t]+$/)) { - this._value = value; - } - - if (typeof this._value === 'string') return this; - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node, level) { - if (nodeType === 'block') { - if (node[0] && node[0][0] !== 's') { - node.unshift(['s', '']); - } - for (var i = 0; i < node.length; i++) { - var nodeItem = node[i]; - if (nodeItem[0] === 'declaration') { - var value = '\n' + new Array(level + 2).join(this._value); - var space = node[i - 1]; - var tail; - - if (space[0] !== 's') { - space = ['s', '']; - tail = node.splice(i); - tail.unshift(space); - Array.prototype.push.apply(node, tail); - i++; - } - space[1] = space[1].replace(/(\n)?([\t ]+)?$/, value); - } - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node, level) { - var result = null; - - if (nodeType === 'declaration') { - if (this._prev !== undefined) { - result = this._prev.replace(/\s*\n/g, ''); - if (level > 0) { - result = result.substr(0, parseInt(result.length / (level + 1), 10)); - } - } else { - result = ''; - } - } - - // Store the previous nodeType - if (nodeType === 's') { - this._prev = node[0]; - } else { - this._prev = undefined; - } - - if (result !== null) { - return result; - } - } - -}; diff --git a/lib/options/sort-order.js b/lib/options/sort-order.js deleted file mode 100644 index cec2ef0f..00000000 --- a/lib/options/sort-order.js +++ /dev/null @@ -1,310 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {Array} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - if (!value) return; - - this._order = {}; - - if (typeof value[0] === 'string') { - value.forEach(function(prop, propIndex) { - this._order[prop] = { group: 0, prop: propIndex }; - }, this); - } else { - value.forEach(function(group, groupIndex) { - group.forEach(function(prop, propIndex) { - this._order[prop] = { group: groupIndex, prop: propIndex }; - }, this); - }, this); - } - - return this; - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - // Types of nodes that can be sorted: - var NODES = ['atruleb', 'atruler', 'atrules', 'commentML', 'commentSL', - 'declaration', 's', 'include']; - // Spaces and comments: - var SC = ['commentML', 'commentSL', 's']; - - var currentNode; - // Sort order of properties: - var order = this._order; - // List of declarations that should be sorted: - var sorted = []; - // list of nodes that should be removed from parent node: - var deleted = []; - // List of spaces and comments that go before declaration/@-rule: - var sc0 = []; - // Value to search in sort order: either a declaration's property name - // (e.g. `color`), or @-rule's special keyword (e.g. `$import`): - var propertyName; - - // Counters for loops: - var i; - var l; - var j; - var nl; - - /** - * Check if there are any comments or spaces before - * the declaration/@-rule. - * @returns {Array} List of nodes with spaces and comments - */ - var checkSC0 = function() { - // List of nodes with spaces and comments: - var sc = []; - // List of nodes that can be later deleted from parent node: - var d = []; - - for (; i < l; i++) { - currentNode = node[i]; - // If there is no node left, - // stop and do nothing with previously found spaces/comments: - if (!currentNode) { - return false; - } - - // Remove any empty lines: - if (currentNode[0] === 's') { - currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n'); - } - - // If the node is declaration or @-rule, stop and return all - // found nodes with spaces and comments (if there are any): - if (SC.indexOf(currentNode[0]) === -1) break; - - sc.push(currentNode); - d.push(i); - } - - deleted = deleted.concat(d); - - return sc; - }; - - /** - * Check if there are any comments or spaces after - * the declaration/@-rule. - * @returns {Array} List of nodes with spaces and comments - * @private - */ - var checkSC1 = function() { - // List of nodes with spaces and comments: - var sc = []; - // List of nodes that can be later deleted from parent node: - var d = []; - // Position of `\n` symbol inside a node with spaces: - var lbIndex; - - // Check every next node: - for (; i < l; i++) { - currentNode = node[i + 1]; - // If there is no node, or it is nor spaces neither comment, stop: - if (!currentNode || SC.indexOf(currentNode[0]) === -1) break; - - // Remove any empty lines: - if (currentNode[0] === 's') { - currentNode[1] = currentNode[1].replace(/\n[\s\t\n\r]*\n/, '\n'); - } - - if (['commentML', 'commentSL'].indexOf(currentNode[0]) > -1) { - sc.push(currentNode); - d.push(i + 1); - continue; - } - - lbIndex = currentNode[1].indexOf('\n'); - - // If there are any line breaks in a node with spaces, stop and - // split the node into two: one with spaces before line break - // and one with `\n` symbol and everything that goes after. - // Combine the first one with declaration/@-rule's node: - if (lbIndex > -1) { - // TODO: Don't push an empty array - sc.push(['s', currentNode[1].substring(0, lbIndex)]); - currentNode[1] = currentNode[1].substring(lbIndex); - break; - } - - sc.push(currentNode); - d.push(i + 1); - } - - deleted = deleted.concat(d); - - return sc; - }; - - /** - * Combine declaration/@-rule's node with other relevant information: - * property index, semicolon, spaces and comments. - * @returns {Object} Extended node - */ - var extendNode = function() { - currentNode = node[i]; - var nextNode = node[i + 1]; - // Object containing current node, all corresponding spaces, - // comments and other information: - var extendedNode; - // Check if current node's property name is in sort order. - // If it is, save information about its indices: - var orderProperty = order[propertyName]; - - extendedNode = { - i: i, - node: currentNode, - sc0: sc0, - delim: [] - }; - - // If the declaration's property is in order's list, save its - // group and property indices. Otherwise set them to 10000, so - // declaration appears at the bottom of a sorted list: - extendedNode.groupIndex = orderProperty && orderProperty.group > -1 ? - orderProperty.group : 10000; - extendedNode.propertyIndex = orderProperty && orderProperty.prop > -1 ? - orderProperty.prop : 10000; - - // Mark current node to remove it later from parent node: - deleted.push(i); - - // If there is `;` right after the declaration, save it with the - // declaration and mark it for removing from parent node: - if (currentNode && nextNode && nextNode[0] === 'declDelim') { - extendedNode.delim.push(nextNode); - deleted.push(i + 1); - i++; - } - - // Save spaces and comments which follow right after the declaration - // and mark them for removing from parent node: - extendedNode.sc1 = checkSC1(); - - return extendedNode; - }; - - // TODO: Think it through! - // Sort properties only inside blocks: - if (nodeType !== 'block') return; - - // Check every child node. - // If it is declaration (property-value pair, e.g. `color: tomato`), - // or @-rule (e.g. `@include nani`), - // combine it with spaces, semicolon and comments and move them from - // current node to a separate list for further sorting: - for (i = 0, l = node.length; i < l; i++) { - if (NODES.indexOf(node[i][0]) === -1) continue; - - // Save preceding spaces and comments, if there are any, and mark - // them for removing from parent node: - sc0 = checkSC0(); - if (!sc0) continue; - - // If spaces/comments are the last nodes, stop and go to sorting: - if (!node[i]) { - deleted.splice(deleted.length - sc0.length, deleted.length + 1); - break; - } - - // Check if the node needs to be sorted: - // it should be a special @-rule (e.g. `@include`) or a declaration - // with a valid property (e.g. `color` or `$width`). - // If not, proceed with the next node: - propertyName = null; - // Look for includes: - if (node[i][0] === 'include') { - propertyName = '$include'; - } else { - for (j = 1, nl = node[i].length; j < nl; j++) { - currentNode = node[i][j]; - if (currentNode[0] === 'property') { - propertyName = currentNode[1][0] === 'variable' ? - '$variable' : currentNode[1][1]; - break; - } else if (currentNode[0] === 'atkeyword' && - currentNode[1][1] === 'import') { // Look for imports - propertyName = '$import'; - break; - } - } - } - - // If current node is not property-value pair or import or include, - // skip it and continue with the next node: - if (!propertyName) { - deleted.splice(deleted.length - sc0.length, deleted.length + 1); - continue; - } - - // Make an extended node and move it to a separate list for further - // sorting: - sorted.push(extendNode()); - } - - // Remove all nodes, that were moved to a `sorted` list, from parent node: - for (i = deleted.length - 1; i > -1; i--) { - node.splice(deleted[i], 1); - } - - // Sort declarations saved for sorting: - sorted.sort(function(a, b) { - // If a's group index is higher than b's group index, in a sorted - // list a appears after b: - if (a.groupIndex !== b.groupIndex) return a.groupIndex - b.groupIndex; - - // If a and b have the same group index, and a's property index is - // higher than b's property index, in a sorted list a appears after - // b: - if (a.propertyIndex !== b.propertyIndex) return a.propertyIndex - b.propertyIndex; - - // If a and b have the same group index and the same property index, - // in a sorted list they appear in the same order they were in - // original array: - return a.i - b.i; - }); - - // Build all nodes back together. First go sorted declarations, then - // everything else: - if (sorted.length > 0) { - for (i = sorted.length - 1, l = -1; i > l; i--) { - currentNode = sorted[i]; - var prevNode = sorted[i - 1]; - sc0 = currentNode.sc0; - var sc1 = currentNode.sc1; - - // Divide declarations from different groups with an empty line: - if (prevNode && currentNode.groupIndex > prevNode.groupIndex) { - if (sc0[0] && sc0[0][0] === 's' && - sc0[0][1].match(/\n/g) && - sc0[0][1].match(/\n/g).length < 2) { - sc0.unshift(['s', '\n']); - } - } - - sc0.reverse(); - sc1.reverse(); - - for (j = 0, nl = sc1.length; j < nl; j++) { - node.unshift(sc1[j]); - } - if (currentNode.delim.length > 0) node.unshift(['declDelim']); - node.unshift(currentNode.node); - for (j = 0, nl = sc0.length; j < nl; j++) { - node.unshift(sc0[j]); - } - } - } - } -}; diff --git a/lib/options/stick-brace.js b/lib/options/stick-brace.js deleted file mode 100644 index 188ee152..00000000 --- a/lib/options/stick-brace.js +++ /dev/null @@ -1,66 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {String|Number} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - delete this._value; - - if (typeof value === 'number' && value === Math.abs(Math.round(value))) { - this._value = new Array(value + 1).join(' '); - } else if (typeof value === 'string' && value.match(/^[ \t\n]*$/)) { - this._value = value; - } - - if (typeof this._value === 'string') return this; - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'selector' || nodeType === 'atruler') { - for (var i = node.length; i--;) { - var nodeItem = node[i]; - if (nodeItem[0] === 'simpleselector' || nodeItem[0] === 'atrulerq') { - if (nodeItem[nodeItem.length - 1][0] === 's') nodeItem.pop(); - nodeItem.push(['s', this._value]); - break; - } - } - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node, level) { - if (nodeType === 'selector' || nodeType === 'atruler') { - for (var i = node.length; i--;) { - var nodeItem = node[i]; - if (nodeItem[0] === 'simpleselector' || nodeItem[0] === 'atrulerq') { - var result = ''; - if (nodeItem[nodeItem.length - 1][0] === 's') { - result = nodeItem[nodeItem.length - 1][1]; - } - if (this._prev !== undefined && this._prev[0] < level) { - result = result.replace(result.replace(this._prev[1], ''), ''); - } - if (this._prev === undefined || this._prev[0] !== level) { - this._prev = [level, result]; - } - return result; - } - } - } - } - -}; diff --git a/lib/options/strip-spaces.js b/lib/options/strip-spaces.js deleted file mode 100644 index 01f3383e..00000000 --- a/lib/options/strip-spaces.js +++ /dev/null @@ -1,66 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {String|Boolean} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - this._value = value === true; - if (!this._value) return; - return this; - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 's') { - node[0] = this._trim(node[0]); - } - if (nodeType === 'stylesheet') { - var lastChild = node[node.length - 1]; - if (lastChild[0] === 's') { - lastChild[1] = this._trim(lastChild[1]) - .replace(/[ \t]+$/, '') - .replace(/[\n]+/g, '\n'); - } - } - }, - - /** - * Trim trailing spaces on each line. - * @private - * @param {String} s Spaceful string - * @returns {String} - */ - _trim: function(s) { - return s.replace(/[ \t]+\n/g, '\n'); - }, - - /** - * Detects the value of an option at the tree node. - * This option is treated as `true` by default, but any trailing space would invalidate it. - * - * @param {String} nodeType - * @param {node} node - */ - _detectDefault: true, - - detect: function(nodeType, node) { - if (nodeType === 's') { - if (node[0].match(/[ \t]\n/)) { - return false; - } - } - if (nodeType === 'stylesheet') { - var lastChild = node[node.length - 1]; - if (lastChild[0] === 's' && lastChild[1] !== '\n' && lastChild[1].match(/^[ \n\t]+$/)) { - return false; - } - } - } -}; diff --git a/lib/options/unitless-zero.js b/lib/options/unitless-zero.js deleted file mode 100644 index b42d3e1e..00000000 --- a/lib/options/unitless-zero.js +++ /dev/null @@ -1,68 +0,0 @@ -module.exports = { - - /** - * Sets handler value. - * - * @param {Boolean} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - if (value === true) { - this._value = value; - return this; - } - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType === 'value' || nodeType === 'braces') { - node.forEach(function(child, index) { - if ( - (child[0] === 'percentage' || - child[0] === 'dimension' && ['cm', 'em', 'ex', 'pt', 'px'].indexOf(child[2][1]) !== -1) && - child[1][1] === '0') { - node[index] = child[1]; - } - }); - } - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - var result = null; - - // If we see a zero with unit and it is not degree, then we don’t have an option - if ( - nodeType === 'percentage' && node[0][1] === '0' || - nodeType === 'dimension' && node[0][1] === '0' && node[1][1] !== 'deg' - ) { - result = false; - } - - // If we see a zero and previous node is not percentage or dimension, then we have an option - if ( - nodeType === 'number' && - node[0] === '0' && - this._prev !== 'percentage' && - this._prev !== 'dimension' - ) { - result = true; - } - - // Store the previous nodeType - this._prev = nodeType; - - if (result !== null) { - return result; - } - } -}; diff --git a/lib/options/vendor-prefix-align.js b/lib/options/vendor-prefix-align.js deleted file mode 100644 index 523916cd..00000000 --- a/lib/options/vendor-prefix-align.js +++ /dev/null @@ -1,260 +0,0 @@ -module.exports = { - - /** - * Internal - * - * Containt vendor-prefixes list. - */ - _prefixesList: [ - 'webkit', - 'moz', - 'ms', - 'o' - ], - - /** - * Internal - * - * Create object which contains info about vendor prefix used in propertyName. - * @param {String} propertyName property name - * @returns {Object|undefined} - */ - _getPrefixInfo: function(propertyName) { - if (!propertyName) return; - - var result = { baseName: propertyName, prefixLength: 0 }; - - this._prefixesList.some(function(prefix) { - // TODO: Why don't we store prefixes with `-`? - prefix = '-' + prefix + '-'; - if (propertyName.indexOf(prefix) !== 0) return; - result = { - baseName: propertyName.substr(prefix.length), - prefixLength: prefix.length - }; - return true; - }); - - return result; - }, - - /** - * Internal - * - * Walk across nodes, and call payload for every node that pass selector check. - * @param {node} node - * @param {function} selector - * @param {function} payload - */ - _walk: function(node, selector, payload) { - node.forEach(function(item, i) { - var info = this._getPrefixInfo(selector(item)); - if (!info) return; - payload(info, i); - }, this); - }, - - /** - * Internal - * - * Return property name. - * e.g. - * for: 'color: #fff' - * returns string: 'color' - * @param {node} node - * @returns {String|undefined} - */ - _getDeclName: function(node) { - if (node[0] !== 'declaration') return; - // TODO: Check that it's not a variable - return node[1][1][1]; - }, - - /** - * Internal - * - * Return property value name. - * e.g. - * for: '-webkit-transition: -webkit-transform 150ms linear' - * returns string: '-webkit-transform', and - * for: 'background: -webkit-linear-gradient(...)' - * returns string: '-webkit-linear-gradient' - * @param {node} node - * @returns {String|undefined} - */ - _getValName: function(node) { - // TODO: Check that `node[3]` is the node we need - if (node[0] !== 'declaration' || !node[3] || !node[3][2]) - return; - if (node[3][2] && node[3][2][0] === 'ident') - return node[3][2][1]; - if (node[3][2] && node[3][2][0] === 'function') - return node[3][2][1][1]; - }, - - /** - * Internal - * - * Update dict which contains info about items align. - * @param {Object} info, - * @param {Object} dict, - * @param {String} whitespaceNode - */ - _updateDict: function(info, dict, whitespaceNode) { - if (info.prefixLength === 0) return; - - var indent = dict[info.baseName] || { prefixLength: 0, baseLength: 0 }; - - dict[info.baseName] = indent.prefixLength > info.prefixLength ? - indent : - { - prefixLength: info.prefixLength, - baseLength: whitespaceNode.substr(whitespaceNode.lastIndexOf('\n') + 1).length - }; - }, - - /** - * Return string with correct number of spaces for info.baseName property. - * @param {Object} info, - * @param {Object} dict, - * @param {String} whitespaceNode - * @returns {String} - */ - _updateIndent: function(info, dict, whitespaceNode) { - if (!dict[info.baseName]) - return whitespaceNode; - - var firstPart = whitespaceNode.substr(0, whitespaceNode.lastIndexOf('\n') + 1 ); - var extraIndent = new Array( - dict[info.baseName].prefixLength - - info.prefixLength + - dict[info.baseName].baseLength + 1).join(' '); - - return firstPart.concat(extraIndent); - }, - - /** - * Sets handler value. - * - * @param {Array} value Option value - * @returns {Object|undefined} - */ - setValue: function(value) { - return value ? this : undefined; - }, - - /** - * Processes tree node. - * @param {String} nodeType - * @param {node} node - */ - process: function(nodeType, node) { - if (nodeType !== 'block') return; - - var dict = {}; - var _this = this; - - // Gathering Info - this._walk(node, this._getDeclName, function(info, i) { - _this._updateDict(info, dict, node[i - 1][1]); - }); - this._walk(node, this._getValName, function(info, i) { - _this._updateDict(info, dict, node[i][3][1][1]); - }); - - // Update nodes - this._walk(node, this._getDeclName, function(info, i) { - node[i - 1][1] = _this._updateIndent(info, dict, node[i - 1][1]); - }); - this._walk(node, this._getValName, function(info, i) { - node[i][3][1][1] = _this._updateIndent(info, dict, node[i][3][1][1]); - }); - }, - - /** - * Detects the value of an option at the tree node. - * - * @param {String} nodeType - * @param {node} node - */ - detect: function(nodeType, node) { - if (nodeType !== 'block') return; - - var result = { - true: 0, - false: 0 - }; - - var maybePrefix = false; - var prevPrefixLength = false; - var prevProp; - var prevSum; - var partialResult = null; - - var getResult = function(node, sum, info, i) { - var prop = info.baseName; - - // If this is the last item in a row and we have a result, then catch it - if (prop !== prevProp && partialResult !== null) { - if (partialResult) { - result.true++; - } else { - result.false++; - } - partialResult = null; - } - - if (prop === prevProp && info.prefixLength !== prevPrefixLength) { - maybePrefix = true; - } else { - maybePrefix = false; - } - - if (maybePrefix && partialResult !== false) { - // If there is prefixed prop, check if the prefixes are aligned, - // but only if we hadn't already catched that it is false - if (sum === prevSum) { - partialResult = true; - } else { - partialResult = false; - } - } - - if (node.length === i + 3 && partialResult !== null) { - // If we're at the last property and have a result, catch it - if (partialResult) { - result.true++; - } else { - result.false++; - } - } - - prevPrefixLength = info.prefixLength; - prevProp = prop; - prevSum = sum; - }; - - // Gathering Info - this._walk(node, this._getDeclName, function(info, i) { - if (node[i - 1]) { - var sum = node[i - 1][1].replace(/^[ \t]*\n+/, '').length + info.prefixLength; - getResult(node, sum, info, i); - } - }); - - this._walk(node, this._getValName, function(info, i) { - if (node[i][3][1]) { - var sum = node[i][3][1][1].replace(/^[ \t]*\n+/, '').length + info.prefixLength; - getResult(node, sum, info, i); - } - }); - - if (result.true > 0 || result.false > 0) { - if (result.true >= result.false) { - return true; - } else { - return false; - } - } - } -}; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..c7f2c458 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,4086 @@ +{ + "name": "csscomb", + "version": "4.2.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/cli": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/@babel/cli/-/cli-7.2.3.tgz", + "integrity": "sha1-GyYuQqPpWdKKs9IFuicY4ZI8/uY=", + "dev": true, + "requires": { + "chokidar": "^2.0.3", + "commander": "^2.8.1", + "convert-source-map": "^1.1.0", + "fs-readdir-recursive": "^1.1.0", + "glob": "^7.0.0", + "lodash": "^4.17.10", + "mkdirp": "^0.5.1", + "output-file-sync": "^2.0.0", + "slash": "^2.0.0", + "source-map": "^0.5.0" + } + }, + "@babel/code-frame": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.0.0.tgz", + "integrity": "sha1-BuKrGb21NThVWaq7W6WXKUgoAPg=", + "dev": true, + "requires": { + "@babel/highlight": "^7.0.0" + } + }, + "@babel/core": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.4.0.tgz", + "integrity": "sha1-JI/Wh0t9dVAQv+YfVXRh1PRG2ek=", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.0", + "@babel/helpers": "^7.4.0", + "@babel/parser": "^7.4.0", + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.0", + "@babel/types": "^7.4.0", + "convert-source-map": "^1.1.0", + "debug": "^4.1.0", + "json5": "^2.1.0", + "lodash": "^4.17.11", + "resolve": "^1.3.2", + "semver": "^5.4.1", + "source-map": "^0.5.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", + "dev": true + } + } + }, + "@babel/generator": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.4.0.tgz", + "integrity": "sha1-wjDnlYmuenKf1GMbne1NwiBBgZY=", + "dev": true, + "requires": { + "@babel/types": "^7.4.0", + "jsesc": "^2.5.1", + "lodash": "^4.17.11", + "source-map": "^0.5.0", + "trim-right": "^1.0.1" + } + }, + "@babel/helper-function-name": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.1.0.tgz", + "integrity": "sha1-oM6wFoX3M1XUNgwSR/WCv6/I/1M=", + "dev": true, + "requires": { + "@babel/helper-get-function-arity": "^7.0.0", + "@babel/template": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-get-function-arity": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.0.0.tgz", + "integrity": "sha1-g1ctQyDipGVyY3NBE8QoaLZOScM=", + "dev": true, + "requires": { + "@babel/types": "^7.0.0" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.0.0.tgz", + "integrity": "sha1-u7P77phmHFaQNCN8wDlnupm08lA=", + "dev": true + }, + "@babel/helper-split-export-declaration": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.4.0.tgz", + "integrity": "sha1-Vxv9UnAfSSkg1jt/c1Aw6aPhC1U=", + "dev": true, + "requires": { + "@babel/types": "^7.4.0" + } + }, + "@babel/helpers": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.4.2.tgz", + "integrity": "sha1-O9+kalUsp371oPhVG+XwhFrpib4=", + "dev": true, + "requires": { + "@babel/template": "^7.4.0", + "@babel/traverse": "^7.4.0", + "@babel/types": "^7.4.0" + } + }, + "@babel/highlight": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.0.0.tgz", + "integrity": "sha1-9xDDjI1Fjm3ZogGvtjf8t4HOmeQ=", + "dev": true, + "requires": { + "chalk": "^2.0.0", + "esutils": "^2.0.2", + "js-tokens": "^4.0.0" + } + }, + "@babel/node": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/@babel/node/-/node-7.2.2.tgz", + "integrity": "sha1-FVfdI1RbONex0DCpwOj7Il2/cKs=", + "dev": true, + "requires": { + "@babel/polyfill": "^7.0.0", + "@babel/register": "^7.0.0", + "commander": "^2.8.1", + "lodash": "^4.17.10", + "v8flags": "^3.1.1" + } + }, + "@babel/parser": { + "version": "7.4.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.4.2.tgz", + "integrity": "sha1-tFIaQAy1qHHqs4kHh7S8EybTjZE=", + "dev": true + }, + "@babel/plugin-transform-destructuring": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-destructuring/-/plugin-transform-destructuring-7.4.0.tgz", + "integrity": "sha1-rLubJBjSkBB9szP01s2Kpq6gA0M=", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/plugin-transform-strict-mode": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-strict-mode/-/plugin-transform-strict-mode-7.2.0.tgz", + "integrity": "sha1-Gv9Lmc6Ye0cVJWaph+lGnke5ZiY=", + "dev": true, + "requires": { + "@babel/helper-plugin-utils": "^7.0.0" + } + }, + "@babel/polyfill": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/polyfill/-/polyfill-7.4.0.tgz", + "integrity": "sha1-kPnWiuNKxCq0tKoDFRhI9TaWAhg=", + "requires": { + "core-js": "^2.6.5", + "regenerator-runtime": "^0.13.2" + } + }, + "@babel/register": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/register/-/register-7.4.0.tgz", + "integrity": "sha1-2dCmIdsmj7FCAPJoWk+JJMgiQEw=", + "dev": true, + "requires": { + "core-js": "^3.0.0", + "find-cache-dir": "^2.0.0", + "lodash": "^4.17.11", + "mkdirp": "^0.5.1", + "pirates": "^4.0.0", + "source-map-support": "^0.5.9" + }, + "dependencies": { + "core-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.0.0.tgz", + "integrity": "sha1-qNv6l40pv8Jjv7ZsVW0MqSTCiVc=", + "dev": true + } + } + }, + "@babel/template": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.4.0.tgz", + "integrity": "sha1-EkdOnAd7rlhcXYNalcCwt5DCXIs=", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/parser": "^7.4.0", + "@babel/types": "^7.4.0" + } + }, + "@babel/traverse": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.4.0.tgz", + "integrity": "sha1-FABpZ90dKzSUzdZQxobbna8N2to=", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "@babel/generator": "^7.4.0", + "@babel/helper-function-name": "^7.1.0", + "@babel/helper-split-export-declaration": "^7.4.0", + "@babel/parser": "^7.4.0", + "@babel/types": "^7.4.0", + "debug": "^4.1.0", + "globals": "^11.1.0", + "lodash": "^4.17.11" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", + "dev": true + } + } + }, + "@babel/types": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.4.0.tgz", + "integrity": "sha1-Zwck930kzObMfYz2RZnVEdFkiUw=", + "dev": true, + "requires": { + "esutils": "^2.0.2", + "lodash": "^4.17.11", + "to-fast-properties": "^2.0.0" + } + }, + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha1-fSWuBbuK0fm2mRCOEJTs14hK3B8=", + "dev": true + }, + "acorn-jsx": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.0.1.tgz", + "integrity": "sha1-MqBk/ZJUKSFqCbFBECv90YX65A4=", + "dev": true + }, + "ajv": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.0.tgz", + "integrity": "sha1-kNDVRDnaWHzX6EO/twRfUL0ivfE=", + "dev": true, + "requires": { + "fast-deep-equal": "^2.0.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-colors": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.3.tgz", + "integrity": "sha1-V9NbhoboUeLMBMQD8cACA5dqGBM=", + "dev": true + }, + "ansi-escapes": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-3.2.0.tgz", + "integrity": "sha1-h4C5j/nb9WOBUtHx/lwde0RCl2s=", + "dev": true + }, + "ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true + }, + "ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha1-QfuyAkPlCxK+DwS43tvwdSDOhB0=", + "dev": true, + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", + "integrity": "sha1-vLJLTzeTTZqnrBe0ra+J58du8us=", + "dev": true, + "optional": true, + "requires": { + "micromatch": "^3.1.4", + "normalize-path": "^2.1.1" + }, + "dependencies": { + "normalize-path": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", + "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=", + "dev": true, + "optional": true, + "requires": { + "remove-trailing-separator": "^1.0.1" + } + } + } + }, + "argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha1-vNZ5HqWuCXJeF+WtmIE0zUCz2RE=", + "dev": true, + "requires": { + "sprintf-js": "~1.0.2" + } + }, + "arr-diff": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", + "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", + "dev": true + }, + "arr-flatten": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", + "integrity": "sha1-NgSLv/TntH4TZkQxbJlmnqWukfE=", + "dev": true + }, + "arr-union": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", + "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=", + "dev": true + }, + "array-unique": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", + "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", + "dev": true + }, + "assign-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", + "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c=", + "dev": true + }, + "astral-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-1.0.0.tgz", + "integrity": "sha1-bIw/uCfdQ+45GPJ7gngqt2WKb9k=", + "dev": true + }, + "async-each": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.2.tgz", + "integrity": "sha1-i4p8oqZY+Sfp8wfW0aQvQZnw9zU=", + "dev": true, + "optional": true + }, + "atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha1-bZUX654DDSQ2ZmZR6GvZ9vE1M8k=", + "dev": true + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base": { + "version": "0.11.2", + "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", + "integrity": "sha1-e95c7RRbbVUakNuH+DxVi060io8=", + "dev": true, + "requires": { + "cache-base": "^1.0.1", + "class-utils": "^0.3.5", + "component-emitter": "^1.2.1", + "define-property": "^1.0.0", + "isobject": "^3.0.1", + "mixin-deep": "^1.2.0", + "pascalcase": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "binary-extensions": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.0.tgz", + "integrity": "sha1-lSPgATBqMkRLkHQj8d4hZCIvarE=", + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha1-PH/L9SnYcibz0vUrlm/1Jx60Qd0=", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", + "integrity": "sha1-WXn9PxTNUxVl5fot8av/8d+u5yk=", + "dev": true, + "requires": { + "arr-flatten": "^1.1.0", + "array-unique": "^0.3.2", + "extend-shallow": "^2.0.1", + "fill-range": "^4.0.0", + "isobject": "^3.0.1", + "repeat-element": "^1.1.2", + "snapdragon": "^0.8.1", + "snapdragon-node": "^2.0.1", + "split-string": "^3.0.2", + "to-regex": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha1-uqVZ7hTO1zRSIputcyZGfGH6vWA=", + "dev": true + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha1-MnE7wCj3XAL9txDXx7zsHyxgcO8=", + "dev": true + }, + "cache-base": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", + "integrity": "sha1-Cn9GQWgxyLZi7jb+TnxZ129marI=", + "dev": true, + "requires": { + "collection-visit": "^1.0.0", + "component-emitter": "^1.2.1", + "get-value": "^2.0.6", + "has-value": "^1.0.0", + "isobject": "^3.0.1", + "set-value": "^2.0.0", + "to-object-path": "^0.3.0", + "union-value": "^1.0.0", + "unset-value": "^1.0.0" + } + }, + "callsites": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.0.0.tgz", + "integrity": "sha1-+361abcq16RYEvk/2UMKPkELPdM=", + "dev": true + }, + "camelcase": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.2.0.tgz", + "integrity": "sha1-51IqvaXtlMwEieG4RmYQ6IQEz0U=", + "dev": true + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha1-zUJUFnelQzPPVBpJEIwUMrRMlCQ=", + "dev": true, + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chardet": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz", + "integrity": "sha1-kAlISfCTfy7twkJdDSip5fDLrZ4=", + "dev": true + }, + "chokidar": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.5.tgz", + "integrity": "sha1-CuhDTZYigaX1bHKGnnnLbZ2GrU0=", + "dev": true, + "optional": true, + "requires": { + "anymatch": "^2.0.0", + "async-each": "^1.0.1", + "braces": "^2.3.2", + "fsevents": "^1.2.7", + "glob-parent": "^3.1.0", + "inherits": "^2.0.3", + "is-binary-path": "^1.0.0", + "is-glob": "^4.0.0", + "normalize-path": "^3.0.0", + "path-is-absolute": "^1.0.0", + "readdirp": "^2.2.1", + "upath": "^1.1.1" + } + }, + "class-utils": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", + "integrity": "sha1-+TNprouafOAv1B+q0MqDAzGQxGM=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "define-property": "^0.2.5", + "isobject": "^3.0.0", + "static-extend": "^0.1.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "cli": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz", + "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=", + "dev": true, + "requires": { + "exit": "0.1.2", + "glob": "^7.1.1" + } + }, + "cli-cursor": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz", + "integrity": "sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=", + "dev": true, + "requires": { + "restore-cursor": "^2.0.0" + } + }, + "cli-width": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/cli-width/-/cli-width-2.2.0.tgz", + "integrity": "sha1-/xnt6Kml5XkyQUewwR8PvLq+1jk=", + "dev": true + }, + "cliui": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-4.1.0.tgz", + "integrity": "sha1-NIQi2+gtgAswIu709qwQvy5NG0k=", + "dev": true, + "requires": { + "string-width": "^2.1.1", + "strip-ansi": "^4.0.0", + "wrap-ansi": "^2.0.0" + } + }, + "code-point-at": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", + "dev": true + }, + "collection-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", + "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA=", + "dev": true, + "requires": { + "map-visit": "^1.0.0", + "object-visit": "^1.0.0" + } + }, + "color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha1-u3GFBpDh8TZWfeYp0tVHHe2kweg=", + "dev": true, + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", + "dev": true + }, + "commander": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.19.0.tgz", + "integrity": "sha1-9hmKqE5bg8RgVLlN3tv+1e6f8So=", + "dev": true + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=", + "dev": true + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "console-browserify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz", + "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=", + "dev": true, + "requires": { + "date-now": "^0.1.4" + } + }, + "convert-source-map": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.6.0.tgz", + "integrity": "sha1-UbU3qMQ+DwTewZk7/83VBOdYrCA=", + "dev": true, + "requires": { + "safe-buffer": "~5.1.1" + } + }, + "copy-descriptor": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", + "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40=", + "dev": true + }, + "core-js": { + "version": "2.6.5", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.5.tgz", + "integrity": "sha1-RLyNJJ5/sv9dAOA0Gn/7lPv2eJU=" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", + "dev": true + }, + "cross-spawn": { + "version": "6.0.5", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz", + "integrity": "sha1-Sl7Hxk364iw6FBJNus3uhG2Ay8Q=", + "dev": true, + "requires": { + "nice-try": "^1.0.4", + "path-key": "^2.0.1", + "semver": "^5.5.0", + "shebang-command": "^1.2.0", + "which": "^1.2.9" + } + }, + "date-now": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz", + "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs=", + "dev": true + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha1-XRKFFd8TT/Mn6QpMk/Tgd6U2NB8=", + "dev": true, + "requires": { + "ms": "2.0.0" + } + }, + "decamelize": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", + "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", + "dev": true + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "dev": true + }, + "deep-is": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha1-z4jabL7ib+bbcJT2HYcMvYTO6fE=", + "dev": true, + "requires": { + "object-keys": "^1.0.12" + } + }, + "define-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", + "integrity": "sha1-1Flono1lS6d+AqgX+HENcCyxbp0=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.2", + "isobject": "^3.0.1" + }, + "dependencies": { + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "detect-file": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", + "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", + "dev": true + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha1-gAwN0eCov7yVg1wgKtIg/jF+WhI=", + "dev": true + }, + "doctrine": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", + "integrity": "sha1-rd6+rXKmV023g2OdyHoSF3OXOWE=", + "dev": true, + "requires": { + "esutils": "^2.0.2" + } + }, + "dom-serializer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz", + "integrity": "sha1-HsQFnihLq+027sKUHUqXChic58A=", + "dev": true, + "requires": { + "domelementtype": "^1.3.0", + "entities": "^1.1.1" + }, + "dependencies": { + "entities": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.1.2.tgz", + "integrity": "sha1-vfpzUplmTfr9NFKe1PhSKidf6lY=", + "dev": true + } + } + }, + "domelementtype": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz", + "integrity": "sha1-0EjESzew0Qp/Kj1f7j9DM9eQSB8=", + "dev": true + }, + "domhandler": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz", + "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=", + "dev": true, + "requires": { + "domelementtype": "1" + } + }, + "domutils": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz", + "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=", + "dev": true, + "requires": { + "dom-serializer": "0", + "domelementtype": "1" + } + }, + "emoji-regex": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz", + "integrity": "sha1-kzoEBShgyF6DwSJHnEdIqOTHIVY=", + "dev": true + }, + "end-of-stream": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.1.tgz", + "integrity": "sha1-7SljTRm6ukY7bOa4CjchPqtx7EM=", + "dev": true, + "requires": { + "once": "^1.4.0" + } + }, + "entities": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz", + "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY=", + "dev": true + }, + "es-abstract": { + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.13.0.tgz", + "integrity": "sha1-rIYUX91QmdjdSVWMy6Lq+biOJOk=", + "dev": true, + "requires": { + "es-to-primitive": "^1.2.0", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "is-callable": "^1.1.4", + "is-regex": "^1.0.4", + "object-keys": "^1.0.12" + } + }, + "es-to-primitive": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.0.tgz", + "integrity": "sha1-7fckeAM0VujdqO8J4ArZZQcH83c=", + "dev": true, + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", + "dev": true + }, + "eslint": { + "version": "5.15.3", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-5.15.3.tgz", + "integrity": "sha1-x5w5CdyKf6NxT7NAwR4w/SUmuLU=", + "dev": true, + "requires": { + "@babel/code-frame": "^7.0.0", + "ajv": "^6.9.1", + "chalk": "^2.1.0", + "cross-spawn": "^6.0.5", + "debug": "^4.0.1", + "doctrine": "^3.0.0", + "eslint-scope": "^4.0.3", + "eslint-utils": "^1.3.1", + "eslint-visitor-keys": "^1.0.0", + "espree": "^5.0.1", + "esquery": "^1.0.1", + "esutils": "^2.0.2", + "file-entry-cache": "^5.0.1", + "functional-red-black-tree": "^1.0.1", + "glob": "^7.1.2", + "globals": "^11.7.0", + "ignore": "^4.0.6", + "import-fresh": "^3.0.0", + "imurmurhash": "^0.1.4", + "inquirer": "^6.2.2", + "js-yaml": "^3.12.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "levn": "^0.3.0", + "lodash": "^4.17.11", + "minimatch": "^3.0.4", + "mkdirp": "^0.5.1", + "natural-compare": "^1.4.0", + "optionator": "^0.8.2", + "path-is-inside": "^1.0.2", + "progress": "^2.0.0", + "regexpp": "^2.0.1", + "semver": "^5.5.1", + "strip-ansi": "^4.0.0", + "strip-json-comments": "^2.0.1", + "table": "^5.2.3", + "text-table": "^0.2.0" + }, + "dependencies": { + "debug": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", + "integrity": "sha1-O3ImAlUQnGtYnO4FDx1RYTlmR5E=", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", + "dev": true + } + } + }, + "eslint-scope": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz", + "integrity": "sha1-ygODMxD2iJoyZHgaqC5j65z+eEg=", + "dev": true, + "requires": { + "esrecurse": "^4.1.0", + "estraverse": "^4.1.1" + } + }, + "eslint-utils": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.3.1.tgz", + "integrity": "sha1-moUbqJ7nxGA0b5fPiTnHKYgn5RI=", + "dev": true + }, + "eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha1-PzGA+y4pEBdxastMnW1bXDSmqB0=", + "dev": true + }, + "espree": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz", + "integrity": "sha1-XWUm+k/H8HiKXPdbFfMDI+L4H3o=", + "dev": true, + "requires": { + "acorn": "^6.0.7", + "acorn-jsx": "^5.0.0", + "eslint-visitor-keys": "^1.0.0" + } + }, + "esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha1-E7BM2z5sXRnfkatph6hpVhmwqnE=", + "dev": true + }, + "esquery": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz", + "integrity": "sha1-QGxRZYsfWZGl+bYrHcJbAOPlxwg=", + "dev": true, + "requires": { + "estraverse": "^4.0.0" + } + }, + "esrecurse": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz", + "integrity": "sha1-AHo7n9vCs7uH5IeeoZyS/b05Qs8=", + "dev": true, + "requires": { + "estraverse": "^4.1.0" + } + }, + "estraverse": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true + }, + "esutils": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true + }, + "execa": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz", + "integrity": "sha1-xiNqW7TfbW8V6I5/AXeYIWdJ3dg=", + "dev": true, + "requires": { + "cross-spawn": "^6.0.0", + "get-stream": "^4.0.0", + "is-stream": "^1.1.0", + "npm-run-path": "^2.0.0", + "p-finally": "^1.0.0", + "signal-exit": "^3.0.0", + "strip-eof": "^1.0.0" + } + }, + "exit": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", + "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=", + "dev": true + }, + "expand-brackets": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", + "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", + "dev": true, + "requires": { + "debug": "^2.3.3", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "posix-character-classes": "^0.1.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "expand-tilde": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", + "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "extend-shallow": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", + "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=", + "dev": true, + "requires": { + "assign-symbols": "^1.0.0", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "external-editor": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.0.3.tgz", + "integrity": "sha1-WGbbKal4Jtvkvzr9JAcOrZ6kOic=", + "dev": true, + "requires": { + "chardet": "^0.7.0", + "iconv-lite": "^0.4.24", + "tmp": "^0.0.33" + } + }, + "extglob": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", + "integrity": "sha1-rQD+TcYSqSMuhxhxHcXLWrAoVUM=", + "dev": true, + "requires": { + "array-unique": "^0.3.2", + "define-property": "^1.0.0", + "expand-brackets": "^2.1.4", + "extend-shallow": "^2.0.1", + "fragment-cache": "^0.2.1", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "fast-deep-equal": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true + }, + "fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true + }, + "figures": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz", + "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=", + "dev": true, + "requires": { + "escape-string-regexp": "^1.0.5" + } + }, + "file-entry-cache": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz", + "integrity": "sha1-yg9u+m3T1WEzP7FFFQZcL6/fQ5w=", + "dev": true, + "requires": { + "flat-cache": "^2.0.1" + } + }, + "fill-range": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", + "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-number": "^3.0.0", + "repeat-string": "^1.6.1", + "to-regex-range": "^2.1.0" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "find-cache-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz", + "integrity": "sha1-jQ+UzRP+Q8bHwmGg2GEVypGMBfc=", + "dev": true, + "requires": { + "commondir": "^1.0.1", + "make-dir": "^2.0.0", + "pkg-dir": "^3.0.0" + } + }, + "find-up": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz", + "integrity": "sha1-SRafHXmTQwZG2mHsxa41XCHJe3M=", + "dev": true, + "requires": { + "locate-path": "^3.0.0" + } + }, + "findup-sync": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", + "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw=", + "dev": true, + "requires": { + "detect-file": "^1.0.0", + "is-glob": "^3.1.0", + "micromatch": "^3.0.4", + "resolve-dir": "^1.0.1" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "flat": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/flat/-/flat-4.1.0.tgz", + "integrity": "sha1-CQvsiwXjnLowl0fx1YjwTbr5jbI=", + "dev": true, + "requires": { + "is-buffer": "~2.0.3" + }, + "dependencies": { + "is-buffer": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.3.tgz", + "integrity": "sha1-Ts8/z3ScvR5HJonhCaxmJhol5yU=", + "dev": true + } + } + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha1-XSltbwS9pEpGMKMBQTvbwuwIXsA=", + "dev": true, + "requires": { + "flatted": "^2.0.0", + "rimraf": "2.6.3", + "write": "1.0.3" + } + }, + "flatted": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.0.tgz", + "integrity": "sha1-VRIrZTbqSWtLRIk+4mCBQdENmRY=", + "dev": true + }, + "for-in": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", + "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=", + "dev": true + }, + "fragment-cache": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", + "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=", + "dev": true, + "requires": { + "map-cache": "^0.2.2" + } + }, + "fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha1-4y/AMKLM7kSmtTcTCNpUvgs5fSc=", + "dev": true + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "fsevents": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.7.tgz", + "integrity": "sha512-Pxm6sI2MeBD7RdD12RYsqaP0nMiwx8eZBXCa6z2L+mRHm2DYrOYwihmhjpkdjUHwQhslWQjRpEgNq4XvBmaAuw==", + "dev": true, + "optional": true, + "requires": { + "nan": "^2.9.2", + "node-pre-gyp": "^0.10.0" + }, + "dependencies": { + "abbrev": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "ansi-regex": { + "version": "2.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "aproba": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "1.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^2.0.6" + } + }, + "balanced-match": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "brace-expansion": { + "version": "1.1.11", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "chownr": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "code-point-at": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "concat-map": { + "version": "0.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "console-control-strings": { + "version": "1.1.0", + "bundled": true, + "dev": true, + "optional": true + }, + "core-util-is": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "debug": { + "version": "2.6.9", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ms": "2.0.0" + } + }, + "deep-extend": { + "version": "0.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "delegates": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "1.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "fs-minipass": { + "version": "1.2.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "fs.realpath": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "gauge": { + "version": "2.7.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.0", + "object-assign": "^4.1.0", + "signal-exit": "^3.0.0", + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1", + "wide-align": "^1.1.0" + } + }, + "glob": { + "version": "7.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "has-unicode": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "iconv-lite": { + "version": "0.4.24", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore-walk": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimatch": "^3.0.4" + } + }, + "inflight": { + "version": "1.0.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "bundled": true, + "dev": true, + "optional": true + }, + "ini": { + "version": "1.3.5", + "bundled": true, + "dev": true, + "optional": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "isarray": { + "version": "1.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "minimatch": { + "version": "3.0.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "0.0.8", + "bundled": true, + "dev": true, + "optional": true + }, + "minipass": { + "version": "2.3.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.2.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minipass": "^2.2.1" + } + }, + "mkdirp": { + "version": "0.5.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "minimist": "0.0.8" + } + }, + "ms": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "needle": { + "version": "2.2.4", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "debug": "^2.1.2", + "iconv-lite": "^0.4.4", + "sax": "^1.2.4" + } + }, + "node-pre-gyp": { + "version": "0.10.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^1.0.2", + "mkdirp": "^0.5.1", + "needle": "^2.2.1", + "nopt": "^4.0.1", + "npm-packlist": "^1.1.6", + "npmlog": "^4.0.2", + "rc": "^1.2.7", + "rimraf": "^2.6.1", + "semver": "^5.3.0", + "tar": "^4" + } + }, + "nopt": { + "version": "4.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "abbrev": "1", + "osenv": "^0.1.4" + } + }, + "npm-bundled": { + "version": "1.0.5", + "bundled": true, + "dev": true, + "optional": true + }, + "npm-packlist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ignore-walk": "^3.0.1", + "npm-bundled": "^1.0.1" + } + }, + "npmlog": { + "version": "4.1.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "~1.1.2", + "console-control-strings": "~1.1.0", + "gauge": "~2.7.3", + "set-blocking": "~2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "object-assign": { + "version": "4.1.1", + "bundled": true, + "dev": true, + "optional": true + }, + "once": { + "version": "1.4.0", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "os-tmpdir": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "osenv": { + "version": "0.1.5", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.0" + } + }, + "path-is-absolute": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "process-nextick-args": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "rc": { + "version": "1.2.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "dependencies": { + "minimist": { + "version": "1.2.0", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "readable-stream": { + "version": "2.3.6", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "rimraf": { + "version": "2.6.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "glob": "^7.1.3" + } + }, + "safe-buffer": { + "version": "5.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "safer-buffer": { + "version": "2.1.2", + "bundled": true, + "dev": true, + "optional": true + }, + "sax": { + "version": "1.2.4", + "bundled": true, + "dev": true, + "optional": true + }, + "semver": { + "version": "5.6.0", + "bundled": true, + "dev": true, + "optional": true + }, + "set-blocking": { + "version": "2.0.0", + "bundled": true, + "dev": true, + "optional": true + }, + "signal-exit": { + "version": "3.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "string-width": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-json-comments": { + "version": "2.0.1", + "bundled": true, + "dev": true, + "optional": true + }, + "tar": { + "version": "4.4.8", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.3.4", + "minizlib": "^1.1.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "wide-align": { + "version": "1.1.3", + "bundled": true, + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wrappy": { + "version": "1.0.2", + "bundled": true, + "dev": true, + "optional": true + }, + "yallist": { + "version": "3.0.3", + "bundled": true, + "dev": true, + "optional": true + } + } + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha1-pWiZ0+o8m6uHS7l3O3xe3pL0iV0=", + "dev": true + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", + "dev": true + }, + "get-caller-file": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", + "integrity": "sha1-+Xj6TJDR3+f/LWvtoqUV5xO9z0o=", + "dev": true + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha1-wbJVV189wh1Zv8ec09K0axw6VLU=", + "dev": true, + "requires": { + "pump": "^3.0.0" + } + }, + "get-value": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", + "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg=", + "dev": true + }, + "glob": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "integrity": "sha1-OWCDLT8VdBCDQtr9OmezMsCWnfE=", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4=", + "dev": true, + "optional": true, + "requires": { + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" + }, + "dependencies": { + "is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.0" + } + } + } + }, + "global-modules": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", + "integrity": "sha1-bXcPDrUjrHgWTXK15xqIdyZcw+o=", + "dev": true, + "requires": { + "global-prefix": "^1.0.1", + "is-windows": "^1.0.1", + "resolve-dir": "^1.0.0" + } + }, + "global-prefix": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", + "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.2", + "homedir-polyfill": "^1.0.1", + "ini": "^1.3.4", + "is-windows": "^1.0.1", + "which": "^1.2.14" + } + }, + "globals": { + "version": "11.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-11.11.0.tgz", + "integrity": "sha1-3Pk3V/ot5Uhvvu1xGFOK33ienC4=", + "dev": true + }, + "gonzales-pe": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/gonzales-pe/-/gonzales-pe-4.2.4.tgz", + "integrity": "sha1-NWrjajEsRv4PECbdbLU5A5+FANI=", + "requires": { + "minimist": "1.1.x" + }, + "dependencies": { + "minimist": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.1.3.tgz", + "integrity": "sha1-O+39kaktOQFvz6ocaB6Pqhoe/ag=" + } + } + }, + "graceful-fs": { + "version": "4.1.15", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz", + "integrity": "sha1-/7cD4QZuig7qpMi4C6klPu77+wA=", + "dev": true + }, + "growl": { + "version": "1.10.5", + "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", + "integrity": "sha1-8nNdwig2dPpnR4sQGBBZNVw2nl4=", + "dev": true + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha1-ci18v8H2qoJB8W3YFOAR4fQeh5Y=", + "dev": true, + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", + "dev": true + }, + "has-symbols": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.0.tgz", + "integrity": "sha1-uhqPGvKg/DllD1yFA2dwQSIGO0Q=", + "dev": true + }, + "has-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", + "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc=", + "dev": true, + "requires": { + "get-value": "^2.0.6", + "has-values": "^1.0.0", + "isobject": "^3.0.0" + } + }, + "has-values": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", + "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "kind-of": "^4.0.0" + }, + "dependencies": { + "kind-of": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", + "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha1-hK5l+n6vsWX922FWauFLrwVmTw8=", + "dev": true + }, + "homedir-polyfill": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", + "integrity": "sha1-dDKYzvTlrz4ZQWH7rcwhUdOgWOg=", + "dev": true, + "requires": { + "parse-passwd": "^1.0.0" + } + }, + "htmlparser2": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz", + "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=", + "dev": true, + "requires": { + "domelementtype": "1", + "domhandler": "2.3", + "domutils": "1.5", + "entities": "1.0", + "readable-stream": "1.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", + "dev": true + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "dev": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + } + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha1-ICK0sl+93CHS9SSXSkdKr+czkIs=", + "dev": true, + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ignore": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", + "integrity": "sha1-dQ49tYYgh7RzfrrIIH/9HvJ7Jfw=", + "dev": true + }, + "import-fresh": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.0.0.tgz", + "integrity": "sha1-o9iX9CDKsOZxI2iX91vBS0iFw5A=", + "dev": true, + "requires": { + "parent-module": "^1.0.0", + "resolve-from": "^4.0.0" + } + }, + "imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", + "dev": true + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, + "ini": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "integrity": "sha1-7uJfVtscnsYIXgwid4CD9Zar+Sc=", + "dev": true + }, + "inquirer": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/inquirer/-/inquirer-6.2.2.tgz", + "integrity": "sha1-RpQRdvZcnrIIBGJxSbdDohjyVAY=", + "dev": true, + "requires": { + "ansi-escapes": "^3.2.0", + "chalk": "^2.4.2", + "cli-cursor": "^2.1.0", + "cli-width": "^2.0.0", + "external-editor": "^3.0.3", + "figures": "^2.0.0", + "lodash": "^4.17.11", + "mute-stream": "0.0.7", + "run-async": "^2.2.0", + "rxjs": "^6.4.0", + "string-width": "^2.1.0", + "strip-ansi": "^5.0.0", + "through": "^2.3.6" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", + "dev": true + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "invert-kv": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-2.0.0.tgz", + "integrity": "sha1-c5P1r6Weyf9fZ6J2INEcIm4+7AI=", + "dev": true + }, + "is-accessor-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", + "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-binary-path": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", + "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg=", + "dev": true, + "optional": true, + "requires": { + "binary-extensions": "^1.0.0" + } + }, + "is-buffer": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", + "integrity": "sha1-76ouqdqg16suoTqXsritUf776L4=", + "dev": true + }, + "is-callable": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.1.4.tgz", + "integrity": "sha1-HhrfIZ4e62hNaR+dagX/DTCiTXU=", + "dev": true + }, + "is-data-descriptor": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", + "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-date-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.1.tgz", + "integrity": "sha1-mqIOtq7rv/d/vTPnTKAbM1gdOhY=", + "dev": true + }, + "is-descriptor": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", + "integrity": "sha1-Nm2CQN3kh8pRgjsaufB6EKeCUco=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^0.1.6", + "is-data-descriptor": "^0.1.4", + "kind-of": "^5.0.0" + }, + "dependencies": { + "kind-of": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", + "integrity": "sha1-cpyR4thXt6QZofmqZWhcTDP1hF0=", + "dev": true + } + } + }, + "is-extendable": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", + "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik=", + "dev": true + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true + }, + "is-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.0.tgz", + "integrity": "sha1-lSHHaEXMJhCoUgPd8ICpWML/q8A=", + "dev": true, + "optional": true, + "requires": { + "is-extglob": "^2.1.1" + } + }, + "is-number": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", + "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=", + "dev": true + }, + "is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha1-LBY7P6+xtgbZ0Xko8FwqHDjgdnc=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "is-promise": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz", + "integrity": "sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=", + "dev": true + }, + "is-regex": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.4.tgz", + "integrity": "sha1-VRdIm1RwkbCTDglWVM7SXul+lJE=", + "dev": true, + "requires": { + "has": "^1.0.1" + } + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", + "dev": true + }, + "is-symbol": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.2.tgz", + "integrity": "sha1-oFX2rlcZLK7jKeeoYBGLSXqVDzg=", + "dev": true, + "requires": { + "has-symbols": "^1.0.0" + } + }, + "is-windows": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-windows/-/is-windows-1.0.2.tgz", + "integrity": "sha1-0YUOuXkezRjmGCzhKjDzlmNLsZ0=", + "dev": true + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", + "dev": true + }, + "isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", + "dev": true + }, + "isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8=", + "dev": true + }, + "js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha1-GSA/tZmR35jjoocFDUZHzerzJJk=", + "dev": true + }, + "js-yaml": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.0.tgz", + "integrity": "sha1-OO5xeKwO6iyX/22W//SxjH2M+Y4=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "jsesc": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", + "integrity": "sha1-gFZNLkg9rPbo7yCWUKZ98/DCg6Q=", + "dev": true + }, + "jshint": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.10.2.tgz", + "integrity": "sha1-7WYmxPgiPJjpSq6mJ2dDVCekmj0=", + "dev": true, + "requires": { + "cli": "~1.0.0", + "console-browserify": "1.1.x", + "exit": "0.1.x", + "htmlparser2": "3.8.x", + "lodash": "~4.17.11", + "minimatch": "~3.0.2", + "shelljs": "0.3.x", + "strip-json-comments": "1.0.x" + }, + "dependencies": { + "strip-json-comments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz", + "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E=", + "dev": true + } + } + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha1-afaofZUTq4u4/mO9sJecRI5oRmA=", + "dev": true + }, + "json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", + "dev": true + }, + "json5": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.1.0.tgz", + "integrity": "sha1-56DGLEgoXGKNIKELhcibuAfDKFA=", + "dev": true, + "requires": { + "minimist": "^1.2.0" + } + }, + "kind-of": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.2.tgz", + "integrity": "sha1-ARRrNqYhjmTljzqNZt5df8b20FE=", + "dev": true + }, + "lcid": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lcid/-/lcid-2.0.0.tgz", + "integrity": "sha1-bvXS32DlL4LrIopMNz6NHzlyU88=", + "dev": true, + "requires": { + "invert-kv": "^2.0.0" + } + }, + "levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + } + }, + "locate-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz", + "integrity": "sha1-2+w7OrdZdYBxtY/ln8QYca8hQA4=", + "dev": true, + "requires": { + "p-locate": "^3.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha1-s56mIp72B+zYniyN8SU2iRysm40=", + "dev": true + }, + "log-symbols": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-2.2.0.tgz", + "integrity": "sha1-V0Dhxdbw39pK2TI7UzIQfva0xAo=", + "dev": true, + "requires": { + "chalk": "^2.0.1" + } + }, + "make-dir": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-2.1.0.tgz", + "integrity": "sha1-XwMQ4YuL6JjMBwCSlaMK5B6R5vU=", + "dev": true, + "requires": { + "pify": "^4.0.1", + "semver": "^5.6.0" + } + }, + "map-age-cleaner": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz", + "integrity": "sha1-fVg6cwZDTAVf5HSw9FB45uG0uSo=", + "dev": true, + "requires": { + "p-defer": "^1.0.0" + } + }, + "map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8=", + "dev": true + }, + "map-visit": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", + "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48=", + "dev": true, + "requires": { + "object-visit": "^1.0.0" + } + }, + "mem": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mem/-/mem-4.2.0.tgz", + "integrity": "sha1-XuBXaA7Zy42tinjYIPmoiXoQICU=", + "dev": true, + "requires": { + "map-age-cleaner": "^0.1.1", + "mimic-fn": "^2.0.0", + "p-is-promise": "^2.0.0" + }, + "dependencies": { + "mimic-fn": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.0.0.tgz", + "integrity": "sha1-CRP/CxIdtE71hIJCw4u7NdRMq94=", + "dev": true + } + } + }, + "micromatch": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", + "integrity": "sha1-cIWbyVyYQJUvNZoGij/En57PrCM=", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "braces": "^2.3.1", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "extglob": "^2.0.4", + "fragment-cache": "^0.2.1", + "kind-of": "^6.0.2", + "nanomatch": "^1.2.9", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.2" + } + }, + "mimic-fn": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-1.2.0.tgz", + "integrity": "sha1-ggyGo5M0ZA6ZUWkovQP8qIBX0CI=", + "dev": true + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha1-UWbihkV/AzBgZL5Ul+jbsMPTIIM=", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "mixin-deep": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.1.tgz", + "integrity": "sha1-pJ5yaNzhoNlpjkUybFYm3zVD0P4=", + "dev": true, + "requires": { + "for-in": "^1.0.2", + "is-extendable": "^1.0.1" + }, + "dependencies": { + "is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha1-p0cPnkJnM9gb2B4RVSZOOjUHyrQ=", + "dev": true, + "requires": { + "is-plain-object": "^2.0.4" + } + } + } + }, + "mkdirp": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", + "dev": true, + "requires": { + "minimist": "0.0.8" + }, + "dependencies": { + "minimist": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", + "dev": true + } + } + }, + "mocha": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-6.0.2.tgz", + "integrity": "sha1-zcGm/fZkcsB5tWBbrFnSmAdwLSw=", + "dev": true, + "requires": { + "ansi-colors": "3.2.3", + "browser-stdout": "1.3.1", + "debug": "3.2.6", + "diff": "3.5.0", + "escape-string-regexp": "1.0.5", + "findup-sync": "2.0.0", + "glob": "7.1.3", + "growl": "1.10.5", + "he": "1.2.0", + "js-yaml": "3.12.0", + "log-symbols": "2.2.0", + "minimatch": "3.0.4", + "mkdirp": "0.5.1", + "ms": "2.1.1", + "node-environment-flags": "1.0.4", + "object.assign": "4.1.0", + "strip-json-comments": "2.0.1", + "supports-color": "6.0.0", + "which": "1.3.1", + "wide-align": "1.1.3", + "yargs": "12.0.5", + "yargs-parser": "11.1.1", + "yargs-unparser": "1.5.0" + }, + "dependencies": { + "debug": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz", + "integrity": "sha1-6D0X3hbYp++3cX7b5fsQE17uYps=", + "dev": true, + "requires": { + "ms": "^2.1.1" + } + }, + "js-yaml": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.12.0.tgz", + "integrity": "sha1-6u1lbsg0TxD1J8a/obbiJE3hZ9E=", + "dev": true, + "requires": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + } + }, + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha1-MKWGTrPrsKZvLr5tcnrwagnYbgo=", + "dev": true + }, + "supports-color": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-6.0.0.tgz", + "integrity": "sha1-ds/nQs8fQbubHCmtAwaMBbTA5Ao=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + } + } + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=", + "dev": true + }, + "mute-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.7.tgz", + "integrity": "sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s=", + "dev": true + }, + "nan": { + "version": "2.13.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.13.1.tgz", + "integrity": "sha512-I6YB/YEuDeUZMmhscXKxGgZlFnhsn5y0hgOZBadkzfTRrZBtJDZeg6eQf7PYMIEclwmorTKK8GztsyOUSVBREA==", + "dev": true, + "optional": true + }, + "nanomatch": { + "version": "1.2.13", + "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", + "integrity": "sha1-uHqKpPwN6P5r6IiVs4mD/yZb0Rk=", + "dev": true, + "requires": { + "arr-diff": "^4.0.0", + "array-unique": "^0.3.2", + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "fragment-cache": "^0.2.1", + "is-windows": "^1.0.2", + "kind-of": "^6.0.2", + "object.pick": "^1.3.0", + "regex-not": "^1.0.0", + "snapdragon": "^0.8.1", + "to-regex": "^3.0.1" + } + }, + "natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", + "dev": true + }, + "nice-try": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/nice-try/-/nice-try-1.0.5.tgz", + "integrity": "sha1-ozeKdpbOfSI+iPybdkvX7xCJ42Y=", + "dev": true + }, + "node-environment-flags": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.4.tgz", + "integrity": "sha1-C3hKZVFCa/wW07IghCTcvCsv8Dg=", + "dev": true, + "requires": { + "object.getownpropertydescriptors": "^2.0.3" + } + }, + "node-modules-regexp": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-modules-regexp/-/node-modules-regexp-1.0.0.tgz", + "integrity": "sha1-jZ2+KJZKSsVxLpExZCEHxx6Q7EA=", + "dev": true + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha1-Dc1p/yOhybEf0JeDFmRKA4ghamU=", + "dev": true, + "optional": true + }, + "npm-run-path": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz", + "integrity": "sha1-NakjLfo11wZ7TLLd8jV7GHFTbF8=", + "dev": true, + "requires": { + "path-key": "^2.0.0" + } + }, + "number-is-nan": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", + "dev": true + }, + "object-copy": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", + "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw=", + "dev": true, + "requires": { + "copy-descriptor": "^0.1.0", + "define-property": "^0.2.5", + "kind-of": "^3.0.3" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "object-keys": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.0.tgz", + "integrity": "sha1-Eb0iNI3S4JagRasG9shbzDQPoDI=", + "dev": true + }, + "object-visit": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", + "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs=", + "dev": true, + "requires": { + "isobject": "^3.0.0" + } + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha1-lovxEA15Vrs8oIbwBvhGs7xACNo=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + } + }, + "object.getownpropertydescriptors": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/object.getownpropertydescriptors/-/object.getownpropertydescriptors-2.0.3.tgz", + "integrity": "sha1-h1jIRvW0B62rDyNuCYbxSwUcqhY=", + "dev": true, + "requires": { + "define-properties": "^1.1.2", + "es-abstract": "^1.5.1" + } + }, + "object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c=", + "dev": true, + "requires": { + "isobject": "^3.0.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "onetime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/onetime/-/onetime-2.0.1.tgz", + "integrity": "sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=", + "dev": true, + "requires": { + "mimic-fn": "^1.0.0" + } + }, + "optionator": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", + "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, + "requires": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.4", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "wordwrap": "~1.0.0" + } + }, + "os-locale": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-3.1.0.tgz", + "integrity": "sha1-qAKm7hfyTBBIOrmTVxnO9O0Wvxo=", + "dev": true, + "requires": { + "execa": "^1.0.0", + "lcid": "^2.0.0", + "mem": "^4.0.0" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", + "dev": true + }, + "output-file-sync": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/output-file-sync/-/output-file-sync-2.0.1.tgz", + "integrity": "sha1-9TEYKC9fVTwnmVQXkrcjpMcUMMA=", + "dev": true, + "requires": { + "graceful-fs": "^4.1.11", + "is-plain-obj": "^1.1.0", + "mkdirp": "^0.5.1" + } + }, + "p-defer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", + "integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", + "dev": true + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=", + "dev": true + }, + "p-is-promise": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-is-promise/-/p-is-promise-2.0.0.tgz", + "integrity": "sha1-dVTj1XIQmofh8/U/an2F0bGU9MU=", + "dev": true + }, + "p-limit": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz", + "integrity": "sha1-QXyZQeYCepq8ulCS3SkE4lW1+8I=", + "dev": true, + "requires": { + "p-try": "^2.0.0" + } + }, + "p-locate": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz", + "integrity": "sha1-Mi1poFwCZLJZl9n0DNiokasAZKQ=", + "dev": true, + "requires": { + "p-limit": "^2.0.0" + } + }, + "p-try": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.1.0.tgz", + "integrity": "sha1-waDxAw6X3gGLsscYkp0q9ZRj5QU=", + "dev": true + }, + "parent-module": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.0.tgz", + "integrity": "sha1-3yUL3FOR9KCF+1idrXYfWta4ZbU=", + "dev": true, + "requires": { + "callsites": "^3.0.0" + } + }, + "parse-passwd": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", + "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", + "dev": true + }, + "pascalcase": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", + "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ=", + "dev": true + }, + "path-dirname": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-dirname/-/path-dirname-1.0.2.tgz", + "integrity": "sha1-zDPSTVJeCZpTiMAzbG4yuRYGCeA=", + "dev": true, + "optional": true + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=", + "dev": true + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-is-inside": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz", + "integrity": "sha1-NlQX3t5EQw0cEa9hAn+s8HS9/FM=", + "dev": true + }, + "path-key": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", + "integrity": "sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A=", + "dev": true + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha1-1i27VnlAXXLEc37FhgDp3c8G0kw=", + "dev": true + }, + "pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha1-SyzSXFDVmHNcUCkiJP2MbfQeMjE=", + "dev": true + }, + "pirates": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.1.tgz", + "integrity": "sha1-ZDqSyviUVm+RsrmG0sZpUKji+4c=", + "dev": true, + "requires": { + "node-modules-regexp": "^1.0.0" + } + }, + "pkg-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", + "integrity": "sha1-J0kCDyOe2ZCIGx9xIQ1R62UjvqM=", + "dev": true, + "requires": { + "find-up": "^3.0.0" + } + }, + "posix-character-classes": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", + "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs=", + "dev": true + }, + "prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true + }, + "process-nextick-args": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "integrity": "sha1-o31zL0JxtKsa0HDTVQjoKQeI/6o=", + "dev": true, + "optional": true + }, + "progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha1-foz42PW48jnBvGi+tOt4Vn1XLvg=", + "dev": true + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha1-tKIRaBW94vTh6mAjVOjHVWUQemQ=", + "dev": true, + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha1-tYsBCsQMIsVldhbI0sLALHv0eew=", + "dev": true + }, + "readable-stream": { + "version": "2.3.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "integrity": "sha1-sRwn2IuP8fvgcGQ8+UsMea4bCq8=", + "dev": true, + "optional": true, + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "readdirp": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", + "integrity": "sha1-DodiKjMlqjPokihcr4tOhGUppSU=", + "dev": true, + "optional": true, + "requires": { + "graceful-fs": "^4.1.11", + "micromatch": "^3.1.10", + "readable-stream": "^2.0.2" + } + }, + "regenerator-runtime": { + "version": "0.13.2", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.2.tgz", + "integrity": "sha1-MuWcmm+5saSv8JtJMMotRHc0NEc=" + }, + "regex-not": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", + "integrity": "sha1-H07OJ+ALC2XgJHpoEOaoXYOldSw=", + "dev": true, + "requires": { + "extend-shallow": "^3.0.2", + "safe-regex": "^1.1.0" + } + }, + "regexpp": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", + "integrity": "sha1-jRnTHPYySCtYkEn4KB+T28uk0H8=", + "dev": true + }, + "remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8=", + "dev": true, + "optional": true + }, + "repeat-element": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", + "integrity": "sha1-eC4NglwMWjuzlzH4Tv7mt0Lmsc4=", + "dev": true + }, + "repeat-string": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", + "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc=", + "dev": true + }, + "require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", + "dev": true + }, + "require-main-filename": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", + "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE=", + "dev": true + }, + "resolve": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.10.0.tgz", + "integrity": "sha1-O9qur0XMB/N1ZW39LlTtCBCxAbo=", + "dev": true, + "requires": { + "path-parse": "^1.0.6" + } + }, + "resolve-dir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", + "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", + "dev": true, + "requires": { + "expand-tilde": "^2.0.0", + "global-modules": "^1.0.0" + } + }, + "resolve-from": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", + "integrity": "sha1-SrzYUq0y3Xuqv+m0DgCjbbXzkuY=", + "dev": true + }, + "resolve-url": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", + "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", + "dev": true + }, + "restore-cursor": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", + "integrity": "sha1-n37ih/gv0ybU/RYpI9YhKe7g368=", + "dev": true, + "requires": { + "onetime": "^2.0.0", + "signal-exit": "^3.0.2" + } + }, + "ret": { + "version": "0.1.15", + "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", + "integrity": "sha1-uKSCXVvbH8P29Twrwz+BOIaBx7w=", + "dev": true + }, + "rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha1-stEE/g2Psnz54KHNqCYt04M8bKs=", + "dev": true, + "requires": { + "glob": "^7.1.3" + } + }, + "run-async": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.3.0.tgz", + "integrity": "sha1-A3GrSuC91yDUFm19/aZP96RFpsA=", + "dev": true, + "requires": { + "is-promise": "^2.1.0" + } + }, + "rxjs": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.4.0.tgz", + "integrity": "sha1-87sP572n+2nerAwW8XtQsLh5BQQ=", + "dev": true, + "requires": { + "tslib": "^1.9.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha1-mR7GnSluAxN0fVm9/St0XDX4go0=", + "dev": true + }, + "safe-regex": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", + "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4=", + "dev": true, + "requires": { + "ret": "~0.1.10" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha1-RPoWGwGHuVSd2Eu5GAL5vYOFzWo=", + "dev": true + }, + "semver": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz", + "integrity": "sha1-fnQlb7qknHWqfHogXMInmcrIAAQ=", + "dev": true + }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", + "dev": true + }, + "set-value": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.0.tgz", + "integrity": "sha1-ca5KiPD+77v1LR6mBPP7MV67YnQ=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.3", + "split-string": "^3.0.1" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "shebang-command": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-1.2.0.tgz", + "integrity": "sha1-RKrGW2lbAzmJaMOfNj/uXer98eo=", + "dev": true, + "requires": { + "shebang-regex": "^1.0.0" + } + }, + "shebang-regex": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-1.0.0.tgz", + "integrity": "sha1-2kL0l0DAtC2yypcoVxyxkMmO/qM=", + "dev": true + }, + "shelljs": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", + "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", + "dev": true + }, + "signal-exit": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", + "dev": true + }, + "slash": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-2.0.0.tgz", + "integrity": "sha1-3lUoUaF1nfOo8gZTVEL17E3eq0Q=", + "dev": true + }, + "slice-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-2.1.0.tgz", + "integrity": "sha1-ys12k0YaY3pXiNkqfdT7oGjoFjY=", + "dev": true, + "requires": { + "ansi-styles": "^3.2.0", + "astral-regex": "^1.0.0", + "is-fullwidth-code-point": "^2.0.0" + } + }, + "snapdragon": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", + "integrity": "sha1-ZJIufFZbDhQgS6GqfWlkJ40lGC0=", + "dev": true, + "requires": { + "base": "^0.11.1", + "debug": "^2.2.0", + "define-property": "^0.2.5", + "extend-shallow": "^2.0.1", + "map-cache": "^0.2.2", + "source-map": "^0.5.6", + "source-map-resolve": "^0.5.0", + "use": "^3.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + }, + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + } + } + }, + "snapdragon-node": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", + "integrity": "sha1-bBdfhv8UvbByRWPo88GwIaKGhTs=", + "dev": true, + "requires": { + "define-property": "^1.0.0", + "isobject": "^3.0.0", + "snapdragon-util": "^3.0.1" + }, + "dependencies": { + "define-property": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", + "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", + "dev": true, + "requires": { + "is-descriptor": "^1.0.0" + } + }, + "is-accessor-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", + "integrity": "sha1-FpwvbT3x+ZJhgHI2XJsOofaHhlY=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-data-descriptor": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", + "integrity": "sha1-2Eh2Mh0Oet0DmQQGq7u9NrqSaMc=", + "dev": true, + "requires": { + "kind-of": "^6.0.0" + } + }, + "is-descriptor": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", + "integrity": "sha1-OxWXRqZmBLBPjIFSS6NlxfFNhuw=", + "dev": true, + "requires": { + "is-accessor-descriptor": "^1.0.0", + "is-data-descriptor": "^1.0.0", + "kind-of": "^6.0.2" + } + } + } + }, + "snapdragon-util": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", + "integrity": "sha1-+VZHlIbyrNeXAGk/b3uAXkWrVuI=", + "dev": true, + "requires": { + "kind-of": "^3.2.0" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=", + "dev": true + }, + "source-map-resolve": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.2.tgz", + "integrity": "sha1-cuLMNAlVQ+Q7LGKyxMENSpBU8lk=", + "dev": true, + "requires": { + "atob": "^2.1.1", + "decode-uri-component": "^0.2.0", + "resolve-url": "^0.2.1", + "source-map-url": "^0.4.0", + "urix": "^0.1.0" + } + }, + "source-map-support": { + "version": "0.5.11", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.11.tgz", + "integrity": "sha1-76ws4IADVdAmMmoMoj4WKurJpOI=", + "dev": true, + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + }, + "dependencies": { + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha1-dHIq8y6WFOnCh6jQu95IteLxomM=", + "dev": true + } + } + }, + "source-map-url": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", + "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=", + "dev": true + }, + "split-string": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", + "integrity": "sha1-fLCd2jqGWFcFxks5pkZgOGguj+I=", + "dev": true, + "requires": { + "extend-shallow": "^3.0.0" + } + }, + "sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "dev": true + }, + "static-extend": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", + "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY=", + "dev": true, + "requires": { + "define-property": "^0.2.5", + "object-copy": "^0.1.0" + }, + "dependencies": { + "define-property": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", + "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", + "dev": true, + "requires": { + "is-descriptor": "^0.1.0" + } + } + } + }, + "string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha1-q5Pyeo3BPSjKyBXEYhQ6bZASrp4=", + "dev": true, + "requires": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha1-nPFhG6YmhdcDCunkujQUnDrwP8g=", + "dev": true, + "optional": true, + "requires": { + "safe-buffer": "~5.1.0" + } + }, + "strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, + "requires": { + "ansi-regex": "^3.0.0" + } + }, + "strip-eof": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-eof/-/strip-eof-1.0.0.tgz", + "integrity": "sha1-u0P/VZim6wXYm1n80SnJgzE2Br8=", + "dev": true + }, + "strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", + "dev": true + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha1-4uaaRKyHcveKHsCzW2id9lMO/I8=", + "dev": true, + "requires": { + "has-flag": "^3.0.0" + } + }, + "table": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/table/-/table-5.2.3.tgz", + "integrity": "sha1-zeDMbrBnUcAJ76sn6Mggyltnt/I=", + "dev": true, + "requires": { + "ajv": "^6.9.1", + "lodash": "^4.17.11", + "slice-ansi": "^2.1.0", + "string-width": "^3.0.0" + }, + "dependencies": { + "ansi-regex": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.0.tgz", + "integrity": "sha1-i5+PCM8ay4Q3Vqg5yox+MWjFGZc=", + "dev": true + }, + "string-width": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-3.1.0.tgz", + "integrity": "sha1-InZ74htirxCBV0MG9prFG2IgOWE=", + "dev": true, + "requires": { + "emoji-regex": "^7.0.1", + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^5.1.0" + } + }, + "strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha1-jJpTb+tq/JYr36WxBKUJHBrZwK4=", + "dev": true, + "requires": { + "ansi-regex": "^4.1.0" + } + } + } + }, + "text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "dev": true + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=", + "dev": true + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha1-bTQzWIl2jSGyvNoKonfO07G/rfk=", + "dev": true, + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=", + "dev": true + }, + "to-object-path": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", + "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68=", + "dev": true, + "requires": { + "kind-of": "^3.0.2" + }, + "dependencies": { + "kind-of": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", + "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", + "dev": true, + "requires": { + "is-buffer": "^1.1.5" + } + } + } + }, + "to-regex": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", + "integrity": "sha1-E8/dmzNlUvMLUfM6iuG0Knp1mc4=", + "dev": true, + "requires": { + "define-property": "^2.0.2", + "extend-shallow": "^3.0.2", + "regex-not": "^1.0.2", + "safe-regex": "^1.1.0" + } + }, + "to-regex-range": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", + "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg=", + "dev": true, + "requires": { + "is-number": "^3.0.0", + "repeat-string": "^1.6.1" + } + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=", + "dev": true + }, + "tslib": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.9.3.tgz", + "integrity": "sha1-1+TdeSRdhUKMTX5IIqeZF5VMooY=", + "dev": true + }, + "type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, + "requires": { + "prelude-ls": "~1.1.2" + } + }, + "union-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.0.tgz", + "integrity": "sha1-XHHDTLW61dzr4+oM0IIHulqhrqQ=", + "dev": true, + "requires": { + "arr-union": "^3.1.0", + "get-value": "^2.0.6", + "is-extendable": "^0.1.1", + "set-value": "^0.4.3" + }, + "dependencies": { + "extend-shallow": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", + "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", + "dev": true, + "requires": { + "is-extendable": "^0.1.0" + } + }, + "set-value": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/set-value/-/set-value-0.4.3.tgz", + "integrity": "sha1-fbCPnT0i3H945Trzw79GZuzfzPE=", + "dev": true, + "requires": { + "extend-shallow": "^2.0.1", + "is-extendable": "^0.1.1", + "is-plain-object": "^2.0.1", + "to-object-path": "^0.3.0" + } + } + } + }, + "unset-value": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", + "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk=", + "dev": true, + "requires": { + "has-value": "^0.3.1", + "isobject": "^3.0.0" + }, + "dependencies": { + "has-value": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", + "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8=", + "dev": true, + "requires": { + "get-value": "^2.0.3", + "has-values": "^0.1.4", + "isobject": "^2.0.0" + }, + "dependencies": { + "isobject": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", + "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk=", + "dev": true, + "requires": { + "isarray": "1.0.0" + } + } + } + }, + "has-values": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", + "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E=", + "dev": true + } + } + }, + "upath": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/upath/-/upath-1.1.2.tgz", + "integrity": "sha1-PbZYYA7a7sy+bbXmhNZ+6MKs0Gg=", + "dev": true, + "optional": true + }, + "uri-js": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", + "integrity": "sha1-lMVA4f93KVbiKZUHwBCupsiDjrA=", + "dev": true, + "requires": { + "punycode": "^2.1.0" + } + }, + "urix": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", + "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI=", + "dev": true + }, + "use": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", + "integrity": "sha1-1QyMrHmhn7wg8pEfVuuXP04QBw8=", + "dev": true + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", + "dev": true, + "optional": true + }, + "uuid": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.3.tgz", + "integrity": "sha1-Z+LoY3lyFVMN/zGOW/nc6/1Hsho=" + }, + "v8flags": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.1.2.tgz", + "integrity": "sha1-/FzQwidCgYHmwpspkuT48dpeDJ8=", + "dev": true, + "requires": { + "homedir-polyfill": "^1.0.1" + } + }, + "vow": { + "version": "0.4.19", + "resolved": "https://registry.npmjs.org/vow/-/vow-0.4.19.tgz", + "integrity": "sha1-zF701rtpctgwgwp8ns+K2DSnxSU=" + }, + "vow-fs": { + "version": "0.3.6", + "resolved": "https://registry.npmjs.org/vow-fs/-/vow-fs-0.3.6.tgz", + "integrity": "sha1-LUxZviLivyYY3fWXq0uqkjvnIA0=", + "requires": { + "glob": "^7.0.5", + "uuid": "^2.0.2", + "vow": "^0.4.7", + "vow-queue": "^0.4.1" + } + }, + "vow-queue": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/vow-queue/-/vow-queue-0.4.3.tgz", + "integrity": "sha1-S6j2S1bpISwNvlfxQFruvVTM540=", + "requires": { + "vow": "^0.4.17" + } + }, + "which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha1-pFBD1U9YBTFtqNYvn1CRjT2nCwo=", + "dev": true, + "requires": { + "isexe": "^2.0.0" + } + }, + "which-module": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.0.tgz", + "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", + "dev": true + }, + "wide-align": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "integrity": "sha1-rgdOa9wMFKQx6ATmJFScYzsABFc=", + "dev": true, + "requires": { + "string-width": "^1.0.2 || 2" + } + }, + "wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true + }, + "wrap-ansi": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", + "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=", + "dev": true, + "requires": { + "string-width": "^1.0.1", + "strip-ansi": "^3.0.1" + }, + "dependencies": { + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", + "dev": true + }, + "is-fullwidth-code-point": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", + "dev": true, + "requires": { + "number-is-nan": "^1.0.0" + } + }, + "string-width": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", + "dev": true, + "requires": { + "code-point-at": "^1.0.0", + "is-fullwidth-code-point": "^1.0.0", + "strip-ansi": "^3.0.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "dev": true, + "requires": { + "ansi-regex": "^2.0.0" + } + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "write": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/write/-/write-1.0.3.tgz", + "integrity": "sha1-CADhRSO5I6OH5BUSPIZWFqrg9cM=", + "dev": true, + "requires": { + "mkdirp": "^0.5.1" + } + }, + "y18n": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz", + "integrity": "sha1-le+U+F7MgdAHwmThkKEg8KPIVms=", + "dev": true + }, + "yargs": { + "version": "12.0.5", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-12.0.5.tgz", + "integrity": "sha1-BfWZe2CWR7ZPZrgeO0sQo2jnrRM=", + "dev": true, + "requires": { + "cliui": "^4.0.0", + "decamelize": "^1.2.0", + "find-up": "^3.0.0", + "get-caller-file": "^1.0.1", + "os-locale": "^3.0.0", + "require-directory": "^2.1.1", + "require-main-filename": "^1.0.1", + "set-blocking": "^2.0.0", + "string-width": "^2.0.0", + "which-module": "^2.0.0", + "y18n": "^3.2.1 || ^4.0.0", + "yargs-parser": "^11.1.1" + } + }, + "yargs-parser": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-11.1.1.tgz", + "integrity": "sha1-h5oIZZc7yp9rq1y987HGfsfTvPQ=", + "dev": true, + "requires": { + "camelcase": "^5.0.0", + "decamelize": "^1.2.0" + } + }, + "yargs-unparser": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-1.5.0.tgz", + "integrity": "sha1-8rsqfoPLyHu5XI5XKCigbJrdbg0=", + "dev": true, + "requires": { + "flat": "^4.1.0", + "lodash": "^4.17.11", + "yargs": "^12.0.5" + } + } + } +} diff --git a/package.json b/package.json index 7f991bd3..d0e74e0f 100644 --- a/package.json +++ b/package.json @@ -1,67 +1,54 @@ { - "name": "csscomb", - "description": "CSS coding style formatter", - "version": "2.0.2", - "homepage": "http://csscomb.com/", - "author": "Mikhail Troshev ", - "repository": "https://github.com/csscomb/csscomb.js", - "maintainers": [ - { - "name": "Mikhail Troshev", - "email": "mishanga@yandex-team.ru", - "web": "http://mishanga.pro/" - }, - { - "name": "Tony Ganch", - "email": "tonyganch+github@gmail.com", - "web": "http://tonyganch.com/" - }, - { - "name": "Slava Oliyanchuk", - "email": "miripiruni@gmail.com", - "web": "http://miripiruni.org/" - } - ], - "contributors": [ - { - "name": "Sergey Puzankov", - "email": "puzankov@yandex-team.ru" - }, - { - "name": "Denis Payase", - "email": "lostsoul@yandex-team.ru" - }, - { - "name": "Igor Novak", - "email": "bezengi@gmail.com" - }, - { - "name": "Roman Komarov", - "email": "kizmarh@ya.ru" - } - ], - "engines": { - "node": ">= 0.10.0" - }, - "dependencies": { - "commander": "2.0.0", - "gonzales-pe": "2.0.x", - "minimatch": "0.2.12", - "vow": "0.3.11", - "vow-fs": "0.2.3" - }, - "devDependencies": { - "jshint-groups": "0.5.3", - "jshint": "2.3.0", - "jscs": "1.0.11", - "mocha": "1.14.0" - }, - "main": "./lib/csscomb.js", - "bin": { - "csscomb": "./bin/csscomb" - }, - "scripts": { - "test": "./node_modules/.bin/jshint-groups && ./node_modules/.bin/jscs . && node test/mocha", - "test-cov": "rm -rf lib-cov && jscoverage lib lib-cov && TEST_COV=true node test/mocha > ./test/test-coverage.html" + "name": "csscomb", + "description": "CSS coding style formatter", + "version": "4.3.0", + "homepage": "http://csscomb.com/", + "author": "Mikhail Troshev ", + "repository": "https://github.com/csscomb/csscomb.js", + "license": "MIT", + "maintainers": [ + { + "name": "Tony Ganch", + "email": "tonyganch+github@gmail.com", + "web": "http://tonyganch.com/" } + ], + "engines": { + "node": ">= 6.0.0" + }, + "dependencies": { + "@babel/polyfill": "^7.4.0", + "gonzales-pe": "4.2.4", + "minimatch": "3.0.4", + "minimist": "1.2.0", + "vow": "0.4.19", + "vow-fs": "0.3.6" + }, + "devDependencies": { + "@babel/cli": "^7.2.3", + "@babel/core": "^7.4.0", + "@babel/node": "^7.2.2", + "@babel/plugin-transform-destructuring": "^7.4.0", + "@babel/plugin-transform-strict-mode": "^7.2.0", + "acorn": "^6.1.1", + "eslint": "^5.15.3", + "jshint": "2.10.2", + "mocha": "6.0.2" + }, + "main": "./lib/csscomb.js", + "files": [ + "bin", + "config", + "lib" + ], + "bin": { + "csscomb": "./bin/csscomb" + }, + "scripts": { + "build": "./scripts/build.sh", + "coverage": "./scripts/coverage.sh", + "prepublish": "./scripts/build.sh", + "test": "./scripts/build.sh && ./scripts/test.sh", + "watch": "./scripts/watch.sh" + } } diff --git a/scripts/build.sh b/scripts/build.sh new file mode 100755 index 00000000..bbfd5f36 --- /dev/null +++ b/scripts/build.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +printf "\n\ +-----------------------\n\ + Building source files\n\ +-----------------------\n\n" +./node_modules/.bin/babel --plugins @babel/plugin-transform-destructuring --loose all src --out-dir lib diff --git a/scripts/coverage.sh b/scripts/coverage.sh new file mode 100755 index 00000000..d0200e61 --- /dev/null +++ b/scripts/coverage.sh @@ -0,0 +1 @@ +rm -rf lib-cov && jscoverage lib lib-cov && TEST_COV=true node test/mocha > ./test/test-coverage.html diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100755 index 00000000..1948c810 --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +EXIT_CODE=0 + +function test { + "$@" + if [ $? -ne 0 ]; then + EXIT_CODE=1 + fi +} + +# Run linters +printf "\n\ +----------------\n\ + Running JSHint\n\ +----------------\n\n" +test ./node_modules/.bin/jshint ./src + +# Run tests +printf "\n\ +---------------\n\ + Running Mocha\n\ +---------------\n\n" +test ./node_modules/.bin/babel-node --plugins @babel/plugin-transform-strict-mode ./test/mocha + +if [ $EXIT_CODE -ne 0 ]; then +printf "\n\ +----------------------------------------------------\n\ + Please, fix errors shown above and run tests again\n\ +----------------------------------------------------\n" +else +printf "\n\ +------------------------\n\ + Everything looks fine!\n\ +------------------------\n" +fi + +exit $EXIT_CODE diff --git a/scripts/watch.sh b/scripts/watch.sh new file mode 100755 index 00000000..2a237f9e --- /dev/null +++ b/scripts/watch.sh @@ -0,0 +1,8 @@ +#!/bin/bash + +printf "\n\ +-----------------------\n\ + Watching source files\n\ +-----------------------\n\n" + +./node_modules/.bin/babel --loose all --watch src --out-dir lib diff --git a/src/cli.js b/src/cli.js new file mode 100644 index 00000000..5965dd9b --- /dev/null +++ b/src/cli.js @@ -0,0 +1,192 @@ +'use strict'; + +/** + * Command line implementation for CSSComb + * + * Usage example: + * ./node_modules/.bin/csscomb [options] [file1 [dir1 [fileN [dirN]]]] + */ +var fs = require('fs'); +var parseArgs = require('minimist'); +var path = require('path'); +var os = require('os'); + +var Comb = require('./csscomb'); +var Errors = require('./errors'); + + +var getInputData = new Promise(function(resolve) { + var input = ''; + process.stdin.resume(); + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(data) { + input += data; + }); + process.stdin.on('end', function() { + resolve(input); + }); +}); + +var comb = new Comb(); +var options = getOptions(); + +if (options.help) { + displayHelp(); + process.exit(0); +} + +if (options.detect) { + const config = detectConfig(); + process.stdout.write(config); + process.exit(0); +} + +var config = getConfig(); +comb.configure(config); + +if (options['tty-mode'] || process.stdin.isTTY) { + processFiles(options._); +} else { + processSTDIN(); +} + + +function getOptions() { + var parserOptions = { + boolean: ['help', 'lint', 'verbose', 'tty-mode'], + alias: { + config: 'c', + detect: 'd', + lint: 'l', + help: 'h', + verbose: 'v', + 'tty-mode': 't' + } + }; + return parseArgs(process.argv.slice(2), parserOptions); +} + +function displayHelp() { + var help = [ + 'NAME', + ' csscomb — Lint and fix style errors in css files', + '', + 'SYNOPSIS', + ' csscomb [options] file.css', + ' cat file.css | csscomb [options] -', + '', + 'OPTIONS', + ' -c, --config [path]', + ' Path to configuration file.', + ' -d, --detect', + ' Run the tool in detect mode, returning detected options.', + ' -l, --lint', + ' Run the tool in linter mode, without modifying files.', + ' -h, --help', + ' Display help message.', + ' -v, --verbose', + ' Whether to print logging info.', + ' -t, --tty-mode', + ' Run the tool in TTY mode using external app (e.g. IDE).', + '' + ]; + process.stdout.write(help.join(os.EOL)); +} + +function detectConfig() { + const config = Comb.detectInFile(options.detect); + return JSON.stringify(config, false, 4); +} + +function getConfig() { + var configPath = options.config && + path.resolve(process.cwd(), options.config) || + Comb.getCustomConfigPath(); + + var config; + if (!fs.existsSync(configPath)) { + config = require('../config/csscomb.json'); + } else if (configPath.match(/\.css$/)) { + config = Comb.detectInFile(configPath); + } else { + config = Comb.getCustomConfig(configPath); + } + + if (!config) { + const errorMessage = Errors.configParsingError(configPath); + process.stderr.write(errorMessage); + process.exit(1); + } + + applyTemplate(config); + if (options.verbose) config.verbose = options.verbose; + if (options.lint) config.lint = options.lint; + + return config; +} + +function applyTemplate(config) { + if (!config.template) return; + + if (!fs.existsSync(config.template)) { + const errorMessage = Errors.missingTemplateFile(config.template); + process.stderr.write(errorMessage); + process.exit(1); + } + + var templateConfig = Comb.detectInFile(config.template); + for (var attrname in templateConfig) { + if (templateConfig.hasOwnProperty(attrname) && !config[attrname]) { + config[attrname] = templateConfig[attrname]; + } + } +} + +function processFiles(files) { + const promises = files.map(file => { + return comb.processPath(file); + }); + + Promise.all(promises).catch(error => { + process.stderr.write(error.stack); + process.exit(1); + }).then(c => { + c = [].concat.apply([], c); + var tbchanged = c.filter(isChanged => { + return isChanged !== undefined; + }).reduce((a, b) => { + return a + b; + }, 0); + + var changed = options.lint ? 0 : tbchanged; + + if (options.verbose) { + let message = [ + `${c.length} file${c.length === 1 ? '' : 's'} processed`, + `${changed} file${changed === 1 ? '' : 's'} fixed`, + '' + ].join(os.EOL); + process.stdout.write(message); + } + + if (options.lint && tbchanged) { + process.exit(1); + } + + process.exit(0); + }); +} + +function processSTDIN() { + getInputData.then(processInputData); +} + +function processInputData(input) { + comb.processString(input).catch(e => { + process.stderr.write(e.message); + process.exit(1); + }).then(output => { + process.stdout.write(output); + process.exit(0); + }); +} diff --git a/src/core.js b/src/core.js new file mode 100644 index 00000000..eea63d2f --- /dev/null +++ b/src/core.js @@ -0,0 +1,470 @@ +'use strict'; + +if (!global._babelPolyfill) + require('@babel/polyfill'); + +let fs = require('fs'); +var os = require('os'); +let gonzales = require('gonzales-pe'); +let minimatch = require('minimatch'); +let Errors = require('./errors'); +let Plugin = require('./plugin'); + +let vow = require('vow'); +let vfs = require('vow-fs'); + +class Comb { + constructor() { + this.config = {}; + this.exclude = []; + // Whether lint mode is on. + this.lint = false; + // List of file paths that should be excluded from processing. + this.pathsToExclude = null; + // List of used plugins. + this.plugins = []; + this.pluginsDependencies = {}; + // List of supported syntaxes. + this.supportedSyntaxes = new Set(); + // Mapping file extensions to syntax + this.syntaxMap = new Map(); + // Whether verbose mode is on. + this.verbose = false; + } + + /** + * Loads configuration from JSON. + * + * @param {!Object} config + * @return {!Comb} + */ + configure(config) { + if (typeof config !== 'object') + // TODO: throw error + throw new Error(); + + this.lint = config.lint; + this.verbose = config.verbose; + if (config.exclude) + this.exclude = config.exclude.map(function(pattern) { + return new minimatch.Minimatch(pattern); + }); + + if (config.syntax) { + for (let key in config.syntax) { + this.syntaxMap.set(key, config.syntax[key]); + } + } + + for (let i = 0, l = this.plugins.length; i < l; i++) { + let plugin = this.plugins[i]; + let name = plugin.name; + if (!config.hasOwnProperty(name)) continue; + + try { + plugin.value = config[name]; + this.config[name] = plugin.value; + } catch (e) { + // TODO: throw error + } + } + + // Chaining. + return this; + } + + /** + * Lints all files in a directory. + * + * @param {String} path + * @returns {Promise} + */ + lintDirectory(path) { + let files = this._getAcceptableFilesFromDirectory(path); + let promises = files.map((file) => this.lintFile(file)); + return Promise.all(promises); + } + + /** + * Lints a single file. + * + * @param {String} path + * @returns {Promise} + */ + lintFile(path) { + let syntax = this._extractSyntax(path); + return this._readFile(path).then((string) => { + return this.lintString(string, {syntax: syntax, filename: path}); + }); + } + + /** + * Lints a file or a directory. + * + * @param {String} path + */ + lintPath(path) { + path = path.replace(/\/$/, ''); + return fs.statSync(path).isDirectory() ? + this.lintDirectory(path) : + this.lintFile(path); + } + + /** + * Lints a string. + * + * @param {String} text + * @param {{context: String, filename: String, syntax: String}} options + * @returns {Promise} Resolves with list of found errors. + */ + lintString(text, options) { + return this._parseString(text, options) + .then(this._lintTree.bind(this)) + .then(errors => { + errors.forEach(error => { + error.context = this._getContext(text, error.line); + }); + + return errors; + }); + } + + _getContext(text, currentLineNumber) { + var LINES_AROUND = 2; + var result = []; + var start = currentLineNumber - 1 - LINES_AROUND; + var end = currentLineNumber + LINES_AROUND; + var lines = text.split(/\r\n|\r|\n/); + + for (var i = start; i < end; i++) { + var line = lines[i]; + if (!line) continue; + var ln = i + 1; + if (ln === currentLineNumber) { + result.push(ln + '*| ' + line); + } else { + result.push(ln + ' | ' + line); + } + } + return result.join(os.EOL); + } + + /** + * Processes directory recursively. + * + * @param {String} path + * @returns {Promise} + */ + processDirectory(path) { + let that = this; + + return vfs.listDir(path).then(function(filenames) { + return vow.all(filenames.map(function(filename) { + let fullname = path + '/' + filename; + return vfs.stat(fullname).then(function(stat) { + if (stat.isDirectory() && that._shouldProcess(fullname)) { + return that.processDirectory(fullname); + } else { + return that.processFile(fullname); + } + }); + })).then(function(results) { + return [].concat.apply([], results); + }); + }); + } + + /** + * Processes single file. + * + * @param {String} path + * @returns {Promise} + */ + processFile(path) { + let that = this; + + if (!this._shouldProcessFile(path)) return; + + return new Promise(resolve => { + vfs.read(path, 'utf8').then(function(data) { + let syntax = that._extractSyntax(path); + that.processString(data, { + syntax: syntax, + filename: path + }).then(function(processedData) { + if (data === processedData) { + if (that.verbose) console.log(' ', path); + resolve(0); + return; + } + + let tick = that.lint ? '!' : '✓'; + if (that.lint) { + if (that.verbose) console.log(tick, path); + resolve(1); + } else { + return vfs.write(path, processedData, 'utf8').then(function() { + if (that.verbose) console.log(tick, path); + resolve(1); + }); + } + }); + }); + }); + } + + /** + * Processes directory or file. + * + * @returns {Promise} + */ + processPath(path) { + let that = this; + path = path.replace(/\/$/, ''); + + return vfs.stat(path).then(function(stat) { + if (stat.isDirectory()) { + return that.processDirectory(path); + } else { + return that.processFile(path); + } + }); + } + + /** + * Processes a string. + * + * @param {String} text + * @param {{context: String, filename: String, syntax: String}} options + * @returns {Promise} Resolves in processed string + */ + processString(text, options) { + return this._parseString(text, options) + .then(this._processTree.bind(this)) + .then((ast) => ast.toString()); + } + + /** + * Add a plugin. + * @param {Object} options + * @return {Comb} + */ + use(options) { + // Check whether plugin with the same is already used. + let pluginName = options.name; + if (this._pluginAlreadyUsed(pluginName)) { + if (this.verbose) + console.warn(Errors.twoPluginsWithSameName(pluginName)); + return; + } + + let plugin = new Plugin(options); + + plugin.syntax.forEach(function(s) { + this.supportedSyntaxes.add(s); + }, this); + + // Sort plugins. + let pluginToRunBefore = plugin.runBefore; + + if (!pluginToRunBefore) { + this.plugins.push(plugin); + } else { + if (this._pluginAlreadyUsed(pluginToRunBefore)) { + let i = this._pluginIndex(pluginToRunBefore); + this.plugins.splice(i, 0, plugin); + } else { + this.plugins.push(plugin); + if (!this.pluginsDependencies[pluginToRunBefore]) + this.pluginsDependencies[pluginToRunBefore] = []; + this.pluginsDependencies[pluginToRunBefore].push(pluginName); + } + } + + let dependents = this.pluginsDependencies[pluginName]; + if (!dependents) return this; + + for (let i = 0, l = dependents.length; i < l; i++) { + let name = dependents[i]; + let x = this._pluginIndex(name); + let plugin = this.plugins[x]; + this.plugins.splice(x, 1); + this.plugins.splice(-1, 0, plugin); + } + + // Chaining. + return this; + } + + _getAcceptableFilesFromDirectory(path) { + if (!this._shouldProcess(path)) return; + + let files = []; + let filesInThisDir = fs.readdirSync(path); + + for (let i = 0, fl = filesInThisDir.length; i < fl; i++) { + let fullname = path + '/' + filesInThisDir[i]; + let stat = fs.statSync(fullname); + if (stat.isDirectory() && this._shouldProcess(fullname)) + files = files.concat(this._getAcceptableFilesFromDirectory(fullname)); + else if (this._shouldProcessFile(fullname)) + files.push(fullname); + } + + return files; + } + + /** + * @param {Node} ast + * @param {String=} filename + * @return {Array} List of errors. + */ + _lintTree(ast, filename) { + let errors = []; + let config = this.config; + + this.plugins.filter(function(plugin) { + return typeof plugin.value !== null && + typeof plugin.lint === 'function' && + plugin.syntax.indexOf(ast.syntax) !== -1; + }).forEach(function(plugin) { + let e = plugin.lint(ast, config); + errors = errors.concat(e); + }); + + if (filename) { + errors.map(function(error) { + error.filename = filename; + return error; + }); + } + + return errors; + } + + _parseString(text, options) { + let syntax = options && options.syntax; + let filename = options && options.filename || ''; + let context = options && options.context; + let tree; + const lint = this.lint; + + if (!text) return new Promise(function(resolve) { + resolve(lint ? [] : text); + }); + + if (!syntax) syntax = 'css'; + this.syntax = syntax; + + return new Promise(function(resolve) { + try { + tree = gonzales.parse(text, {syntax: syntax, rule: context}); + resolve(tree, filename); + } catch (e) { + let version = require('../package.json').version; + let message = filename ? [filename] : []; + message.push(e.message); + message.push('CSScomb Core version: ' + version); + e.stack = e.message = message.join(os.EOL); + throw e; + } + }); + } + + _pluginAlreadyUsed(name) { + return this._pluginIndex(name) !== -1; + } + + _pluginIndex(name) { + let index = -1; + this.plugins.some(function(plugin, i) { + if (plugin.name === name) { + index = i; + return true; + } + }); + return index; + } + + /** + * @param {Node} ast + * @return {Node} Transformed AST + */ + _processTree(ast) { + let config = this.config; + + this.plugins.filter(function(plugin) { + return plugin.value !== null && + typeof plugin.process === 'function' && + plugin.syntax.indexOf(ast.syntax) !== -1; + }).forEach(function(plugin) { + plugin.process(ast, config); + }); + + return ast; + } + + _readFile(path) { + return new Promise((resolve, reject) => { + if (!this._shouldProcessFile(path)) reject(); + + fs.readFile(path, 'utf8', function(e, string) { + if (e) reject(); + resolve(string); + }); + }); + } + + /** + * Checks if path is not present in `exclude` list. + * + * @param {String} path + * @returns {Boolean} False if specified path is present in `exclude` list. + * Otherwise returns true. + */ + _shouldProcess(path) { + path = path.replace(/\/$/, ''); + if (!fs.existsSync(path)) { + console.warn('Path ' + path + ' was not found.'); + return false; + } + + path = path.replace(/^\.\//, ''); + return this.exclude.every(function(e) { + return !e.match(path); + }); + } + + /** + * Checks if specified path is not present in `exclude` list and it has one of + * acceptable syntaxes. + * + * @param {String} path + * @returns {Boolean} False if the path either has unacceptable extension or + * is present in `exclude` list. True if everything is ok. + */ + _shouldProcessFile(path) { + // Get file's extension: + var syntax = this._extractSyntax(path); + + // Check if syntax is supported. If not, ignore the file: + if (!this.supportedSyntaxes.has(syntax)) + return false; + + return this._shouldProcess(path); + } + + /** + * Extract syntax by file path + * + * @param {String} path + * @returns {String} syntax + */ + _extractSyntax(path) { + var extension = path.split('.').pop(); + + return this.syntaxMap.get('.' + extension) || extension; + } +} + +module.exports = Comb; diff --git a/src/csscomb.js b/src/csscomb.js new file mode 100644 index 00000000..a698767a --- /dev/null +++ b/src/csscomb.js @@ -0,0 +1,314 @@ +'use strict'; + +let gonzales = require('gonzales-pe'); +let fs = require('fs'); +let Comb = require('./core'); +let format = require('./format'); +let path = require('path'); + +/** + * @param {String|Object} config + * @constructor + * @name CSScomb + */ +let CSScomb = function(config) { + let comb = new Comb(); + + // Add plugins. + fs.readdirSync(__dirname + '/options').map(function(option) { + return require('./options/' + option); + }).forEach(function(option) { + comb.use(option); + }); + + // If config was passed, configure: + if (typeof config === 'string') { + config = CSScomb.getConfig(config); + } + if (typeof config === 'object') { + comb.configure(config); + } + + // Chaining. + return comb; +}; + +/** + * STATIC METHODS + * Methods that can be called without creating an instance: + * - detectInFile; + * - detectInString; + * - getConfig; + * - getCustomConfig; + * - getCustomConfigPath; + * For example: `CSScomb.getConfig('zen')` + */ + +/** + * Detects the options in the given file + * + * @param {String} file Path to the stylesheet + * @param {Array} options List of options to detect + * @returns {Object} Detected options + */ +CSScomb.detectInFile = function detectInFile(file, options) { + var stylesheet = fs.readFileSync(file, 'utf8'); + return CSScomb.detectInString(stylesheet, options); +}; + +/** + * Detects the options in the given string + * + * @param {String} text Stylesheet + * @param {Array} options List of options to detect + * @returns {Object} Detected options + */ +CSScomb.detectInString = function detectInString(text, options) { + var result; + var handlers = []; + + if (!text) return text; + + var optionNames = fs.readdirSync(__dirname + '/options'); + optionNames.forEach(function(option) { + option = option.slice(0, -3); + if (options && options.indexOf(option) < 0) return; + try { + handlers.push(getHandler(option)); + } catch (e) { + let message = `\nFailed to load "${option}" option:\n${e.message}`; + console.warn(message); + } + }); + + var tree = cssToAST(text); + var detectedOptions = detectInTree(tree, handlers); + result = getDetectedOptions(detectedOptions, handlers); + + // Handle conflicting options with spaces around braces: + var blockIndent = result['block-indent']; + var spaceAfterOpeningBrace = result['space-after-opening-brace']; + + if (typeof blockIndent === 'string' && + spaceAfterOpeningBrace && + spaceAfterOpeningBrace.indexOf('\n') > -1) { + result['space-after-opening-brace'] = '\n'; + } + + return result; +}; + +/** + * Gets one of configuration files from configs' directory. + * + * @param {String} name Config's name, e.g. 'yandex' + * @returns {Object} Configuration object + */ +CSScomb.getConfig = function getConfig(name) { + const DEFAULT_CONFIG_NAME = 'csscomb'; + name = name || DEFAULT_CONFIG_NAME; + + if (typeof name !== 'string') { + throw new Error('Config name must be a string.'); + } + + let CONFIG_DIR_PATH = '../config'; + let dir = `${__dirname}/${CONFIG_DIR_PATH}`; + let availableConfigsNames = fs.readdirSync(dir) + .map(function(configFileName) { + return configFileName.split('.')[0]; // Strip file extension(s) + }); + + if (availableConfigsNames.indexOf(name) < 0) { + let configsNamesAsString = availableConfigsNames + .map(function(configName) { + return '\'' + configName + '\''; + }) + .join(', '); + let message = `"${name}" is not a valid config name. Try one + of the following: ${configsNamesAsString}.`; + throw new Error(format(message)); + } + + return require(CONFIG_DIR_PATH + '/' + name + '.json'); +}; + +/** + * Gets configuration from provided config path or looks for it in common + * places. + * + * @param {String} [configPath] + * @returns {Object|null} + */ +CSScomb.getCustomConfig = function getCustomConfig(configPath) { + var config; + configPath = configPath || CSScomb.getCustomConfigPath(); + + try { + config = JSON.parse(fs.readFileSync(configPath, 'utf8')); + } catch (e) { + config = null; + } + + return config; +}; + +/** + * Looks for a config file: recursively from current (process) directory + * up to $HOME dir + * If no custom config file is found, return `null`. + * + * @param {String} [configPath] + * @returns {String | null} + */ +CSScomb.getCustomConfigPath = function getCustomConfigPath(configPath) { + var HOME = process.env.HOME || + process.env.HOMEPATH || + process.env.USERPROFILE; + + configPath = configPath || path.join(process.cwd(), '.csscomb.json'); + + // If we've finally found a config, return its path: + if (fs.existsSync(configPath)) return fs.realpathSync(configPath); + + // If we are in HOME dir already and yet no config file, return a default + // one from our package. + // If project is located not under HOME, compare to root instead. + // Since there appears to be no good way to get root path in + // Windows, assume that if current dir has no parent dir, we're in + // root. + var dirname = path.dirname(configPath); + var parentDirname = path.dirname(dirname); + if (dirname === HOME || dirname === parentDirname) return null; + + // If there is no config in this directory, go one level up and look for + // a config there: + configPath = path.join(parentDirname, '.csscomb.json'); + return CSScomb.getCustomConfigPath(configPath); +}; + +/** + * Converts CSS string to AST. + * + * @param {String} text CSS string + * @param {String} [syntax] Syntax name (e.g., `scss`) + * @param {String} [filename] + * @returns {Array} AST + */ +function cssToAST(text, syntax, filename) { + var string = JSON.stringify; + var fileInfo = filename ? ' at ' + filename : ''; + var tree; + + try { + tree = gonzales.parse(text, {syntax: syntax}); + } catch (e) { + throw new Error('Parsing error' + fileInfo + ': ' + e.message); + } + + // TODO: When can tree be undefined? + if (typeof tree === 'undefined') { + let message = `Undefined tree ${fileInfo}: ${string(text)} => + ${string(tree)}`; + throw new Error(format(message)); + } + + return tree; +} + +/** + * Processes tree and detects options. + * + * @param {Array} tree + * @param {Array} handlers List of options that we should look for + * @returns {Object} Map with detected options and all variants of possible + * values + */ +function detectInTree(tree, handlers) { + var detectedOptions = {}; + // We walk across complete tree for each handler, + // because we need strictly maintain order in which handlers work, + // despite fact that handlers work on different level of the tree. + handlers.forEach(function(handler) { + detectedOptions[handler.name] = handler.detect(tree); + }); + return detectedOptions; +} + +/** + * Gets the detected options. + * + * @param {Object} detected + * @param {Array} handlers + * @returns {Object} + */ +function getDetectedOptions(detected, handlers) { + var options = {}; + Object.keys(detected).forEach(function(option) { + // List of all the detected variants from the stylesheet + // for the given option: + var values = detected[option]; + var i; + if (!values.length) { + // If there are no values for the option, check if there is + // a default one: + for (i = handlers.length; i--;) { + if (handlers[i].name === option && + handlers[i].detectDefault !== undefined) { + options[option] = handlers[i].detectDefault; + break; + } + } + } else if (values.length === 1) { + options[option] = values[0]; + } else { + // If there are more than one value for the option, find + // the most popular one; `variants` would be populated + // with the popularity for different values. + var variants = {}; + var bestGuess = null; + var maximum = 0; + for (i = values.length; i--;) { + var currentValue = values[i]; + // Count the current value: + if (variants[currentValue]) { + variants[currentValue]++; + } else { + variants[currentValue] = 1; + } + // If the current variant is the most popular one, treat + // it as the best guess: + if (variants[currentValue] >= maximum) { + maximum = variants[currentValue]; + bestGuess = currentValue; + } + } + if (bestGuess !== null) { + options[option] = bestGuess; + } + } + }); + + return options; +} + +/** + * Gets option's data needed for detection + * + * @param {String} optionName + * @returns {Object} Object with option's name, link to `detect()` method + * and default value for the case when nothing can be detected + */ +function getHandler(optionName) { + var option = require('./options/' + optionName); + if (!option.detect) + throw new Error('Option does not have `detect()` method.'); + + return { + name: option.name, + detect: option.detect, + detectDefault: option.detectDefault + }; +} + +module.exports = CSScomb; diff --git a/src/errors.js b/src/errors.js new file mode 100644 index 00000000..5dc2e4e5 --- /dev/null +++ b/src/errors.js @@ -0,0 +1,71 @@ +'use strict'; + + +let format = require('./format'); + +module.exports = { + configParsingError(configPath) { + return `Error parsing configuration file ${configPath}.`; + }, + + implementSetValue(valueType) { + if (typeof valueType === 'undefined') throw new Error(); + + return format(`If you see this message and you are not + a developer adding a new option, please open an issue here: + https://github.com/csscomb/core/issues/new\n + For option to accept values of type "${valueType}" + you need to implement custom \`setValue()\` method.`); + }, + + missingName() { + return 'Plugin must have a valid \`name\` property.'; + }, + + missingSetValue() { + return format(`Plugin must either implemet \`setValue()\` method + or provide \`accepts\` object with acceptable values.`); + }, + + missingSyntax() { + return 'Plugin must list supported syntaxes.'; + }, + + missingTemplateFile(file) { + return format(`Template configuration file ${file} + was not found.`); + }, + + twoPluginsWithSameName(pluginName) { + if (typeof pluginName === 'undefined') throw new Error(); + + return format(`You're trying to use one plugin twice: + ${pluginName}. Please make sure there are not two different + plugins with the same name.`); + }, + + unacceptableBoolean(pattern) { + if (typeof pattern === 'undefined') throw new Error(); + + return `Value must be one of the following: ${pattern.join(', ')}.`; + }, + + unacceptableNumber() { + return 'Value must be an integer.'; + }, + + unacceptableString(pattern) { + if (typeof pattern === 'undefined') throw new Error(); + + return `Value must match pattern ${pattern}.`; + }, + + unacceptableValueType(valueType, accepts) { + if (typeof valueType === 'undefined' || + typeof accepts === 'undefined') throw new Error(); + + return format(`The option does not accept values of type + ${valueType}.\nValue\'s type must be one the following: + ${Object.keys(accepts).join(', ')}.`); + } +}; diff --git a/src/format.js b/src/format.js new file mode 100644 index 00000000..261df4c4 --- /dev/null +++ b/src/format.js @@ -0,0 +1,5 @@ +'use strict'; + +module.exports = function(string) { + return string.replace(/\n\s+/gm, ' '); +}; diff --git a/src/options/always-semicolon.js b/src/options/always-semicolon.js new file mode 100644 index 00000000..32628216 --- /dev/null +++ b/src/options/always-semicolon.js @@ -0,0 +1,147 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +let option = { + /** + * Option's name as it's used in config. + * @type {String} + */ + get name() { + return 'always-semicolon'; + }, + + /** + * List of syntaxes that are supported by this option. + * @type {Array} + */ + get syntax() { + return ['css', 'less', 'sass', 'scss']; + }, + + /** + * Types of values this option accepts in config. + * @type {Object} + */ + get accepts() { + return { + boolean: [true] + }; + }, + + /** + * Checks ast for code style errors. + * + * @param {Node} ast + * @return {Array?} List of found errors. + */ + lint(ast) { + var errors = []; + + ast.traverseByType('block', (block) => { + block.eachFor((currentNode) => { + var nodeWithoutSemicolon; + // Skip nodes that already have `;` at the end: + if (currentNode.is('declarationDelimiter')) return null; + + // Add semicolon only after declarations and includes. + // If current node is include, insert semicolon right into it. + // If it's declaration, look for value node: + if (currentNode.is('include') || + currentNode.is('extend')) { + nodeWithoutSemicolon = currentNode; + } else if (currentNode.is('declaration')) { + nodeWithoutSemicolon = currentNode.last('value'); + } else { + return; + } + + errors.push({ + message: 'Missing semicolon', + line: nodeWithoutSemicolon.end.line, + column: nodeWithoutSemicolon.end.column + 1 + }); + + // Stop looping through block's children: + return null; + }); + }); + + return errors; + }, + + /** + * Processes ast and fixes found code style errors. + * @param {Node} ast + */ + process(ast) { + var nodeWithoutSemicolon; + + ast.traverseByType('block', (block) => { + block.eachFor((currentNode) => { + // Skip nodes that already have `;` at the end: + if (currentNode.is('declarationDelimiter')) return null; + + // Add semicolon only after declarations and includes. + // If current node is include, insert semicolon right into it. + // If it's declaration, look for value node: + if (currentNode.is('include') || + currentNode.is('extend')) { + nodeWithoutSemicolon = currentNode; + } else if (currentNode.is('declaration')) { + nodeWithoutSemicolon = currentNode.last('value'); + } else { + return; + } + + // Check if there are spaces and comments at the end of the node + for (var j = nodeWithoutSemicolon.length; j--;) { + var lastNode = nodeWithoutSemicolon.get(j); + + // If the node's last child is block, do not add semicolon: + // TODO: Add syntax check and run the code only for scss + if (lastNode.is('block')) { + return null; + } else if (!lastNode.is('space') && + !lastNode.is('multilineComment') && + !lastNode.is('singlelineComment')) { + j++; + break; + } + } + + var declDelim = gonzales.createNode({ + type: 'declarationDelimiter', + content: ';' + }); + nodeWithoutSemicolon.insert(j, declDelim); + return null; + }); + }); + }, + + /** + * Detects the value of this option in ast. + * @param {Node} ast + * @return {Array?} List of detected values + */ + detect(ast) { + var detected = []; + + ast.traverseByType('block', (block) => { + block.eachFor((node) => { + if (node.is('declarationDelimiter')) { + detected.push(true); + return null; + } else if (node.is('declaration')) { + detected.push(false); + return null; + } + }); + }); + + return detected; + } +}; + +module.exports = option; diff --git a/src/options/block-indent.js b/src/options/block-indent.js new file mode 100644 index 00000000..245211e5 --- /dev/null +++ b/src/options/block-indent.js @@ -0,0 +1,142 @@ +'use strict'; + +let option = { + /** + * Option's name as it's used in config. + * @type {String} + */ + get name() { + return 'block-indent'; + }, + + /** + * Name of option that must run after this option. + * @type {String} + */ + get runBefore() { + return 'sort-order'; + }, + + /** + * List of syntaxes that are supported by this option. + * @type {Array} + */ + get syntax() { + return ['css', 'less', 'sass', 'scss']; + }, + + /** + * Types of values this option accepts in config. + * @type {Object} + */ + get accepts() { + return { + number: true, + string: /^[ \t]*$/ + }; + }, + + /** + * Processes ast and fixes found code style errors. + * @param {Node} ast + */ + process(ast) { + ast.eachFor('space', function(whitespaceNode, i) { + var spaces = whitespaceNode.content.replace(/\n[ \t]+/gm, '\n'); + + if (spaces === '') { + ast.removeChild(i); + } else { + whitespaceNode.content = spaces; + } + }); + + this._processNode(ast, 0); + }, + + /** + * Detects the value of this option in ast. + * @param {Node} ast + * @return {Array} List of detected values + */ + detect(ast) { + var detected = []; + + ast.traverse(function(node) { + // Continue only with non-empty {...} blocks: + if (!node.is('atrulers') && !node.is('block') || !node.length) + return; + + node.eachFor('space', function(whitespaceNode) { + var spaces = whitespaceNode.content; + var lastIndex = spaces.lastIndexOf('\n'); + + // Do not continue if there is no line break: + if (lastIndex < 0) return; + + // Number of spaces from beginning of line: + var spacesLength = spaces.slice(lastIndex + 1).length + 1; + detected.push(new Array(spacesLength).join(' ')); + }); + }); + + return detected; + }, + + /** + * @param {Node} node + * @param {Number} level + */ + _processNode(node, level) { + var that = this; + + node.forEach(function(n) { + if (node.syntax === 'sass' && n.is('block')) { + that._processSassBlock(n, level); + } + + // Continue only with space nodes inside {...}: + if (node.syntax !== 'sass' && level !== 0 && n.is('space')) { + that._processSpaceNode(n, level); + } + + if (n.is('block') || n.is('atrulers')) level++; + + that._processNode(n, level); + }); + }, + + /** + * @param {Node} node + * @param {Number} level + */ + _processSassBlock(node, level) { + var value = this.value; + + node.eachFor('space', function(whitespaceNode) { + if (whitespaceNode.content === '\n') return; + + var spaces = whitespaceNode.content.replace(/[ \t]/gm, ''); + spaces += new Array(level + 2).join(value); + whitespaceNode.content = spaces; + }); + }, + + /** + * @param {Node} node + * @param {Number} level + */ + _processSpaceNode(node, level) { + var value = this.value; + + // Remove all whitespaces and tabs, leave only new lines: + var spaces = node.content.replace(/[ \t]/gm, ''); + + if (!spaces) return; + + spaces += new Array(level + 1).join(value); + node.content = spaces; + } +}; + +module.exports = option; diff --git a/src/options/color-case.js b/src/options/color-case.js new file mode 100644 index 00000000..b29ca12b --- /dev/null +++ b/src/options/color-case.js @@ -0,0 +1,64 @@ +'use strict'; + +let option = { + /** + * Option's name as it's used in config. + * @type {String} + */ + get name() { + return 'color-case'; + }, + + /** + * List of syntaxes that are supported by this option. + * @type {Array} + */ + get syntax() { + return ['css', 'less', 'sass', 'scss']; + }, + + /** + * Types of values this option accepts in config. + * @type {Object} + */ + get accepts() { + return { + string: /^lower|upper$/ + }; + }, + + /** + * Processes ast and fixes found code style errors. + * @param {Node} ast + */ + process(ast) { + var value = this.value; + + ast.traverseByType('color', function(color) { + color.content = value === 'lower' ? + color.content.toLowerCase() : + color.content.toUpperCase(); + }); + }, + + /** + * Detects the value of this option in ast. + * @param {Node} ast + * @return {Array} List of detected values + */ + detect(ast) { + var detected = []; + + ast.traverseByType('color', function(color) { + if (color.content.match(/^[^A-F]*[a-f][^A-F]*$/)) { + detected.push('lower'); + } else if (color.content.match(/^[^a-f]*[A-F][^a-f]*$/)) { + detected.push('upper'); + } + }); + + return detected; + } +}; + +module.exports = option; diff --git a/src/options/color-shorthand.js b/src/options/color-shorthand.js new file mode 100644 index 00000000..5abbfdca --- /dev/null +++ b/src/options/color-shorthand.js @@ -0,0 +1,64 @@ +'use strict'; + +let option = { + /** + * Option's name as it's used in config. + * @type {String} + */ + get name() { + return 'color-shorthand'; + }, + + /** + * List of syntaxes that are supported by this option. + * @type {Array} + */ + get syntax() { + return ['css', 'less', 'sass', 'scss']; + }, + + /** + * Types of values this option accepts in config. + * @type {Object} + */ + get accepts() { + return { + boolean: [true, false] + }; + }, + + /** + * Processes ast and fixes found code style errors. + * @param {Node} ast + */ + process(ast) { + var value = this.value; + + ast.traverseByType('color', function(color) { + color.content = value ? + color.content.replace(/(\w)\1(\w)\2(\w)\3/i, '$1$2$3') : + color.content.replace(/^(\w)(\w)(\w)$/, '$1$1$2$2$3$3'); + }); + }, + + /** + * Detects the value of this option in ast. + * @param {Node} ast + * @return {Array} List of detected values + */ + detect(ast) { + var detected = []; + + ast.traverseByType('color', function(color) { + if (color.content.match(/^\w{3}$/)) { + detected.push(true); + } else if (color.content.match(/^(\w)\1(\w)\2(\w)\3$/)) { + detected.push(false); + } + }); + + return detected; + } +}; + +module.exports = option; diff --git a/src/options/element-case.js b/src/options/element-case.js new file mode 100644 index 00000000..3bd7a50c --- /dev/null +++ b/src/options/element-case.js @@ -0,0 +1,76 @@ +'use strict'; + +let option = { + /** + * Option's name as it's used in config. + * @type {String} + */ + get name() { + return 'element-case'; + }, + + /** + * List of syntaxes that are supported by this option. + * @type {Array} + */ + get syntax() { + return ['css', 'less', 'sass', 'scss']; + }, + + /** + * Types of values this option accepts in config. + * @type {Object} + */ + get accepts() { + return { + string: /^lower|upper$/ + }; + }, + + /** + * Processes ast and fixes found code style errors. + * @param {Node} ast + */ + process(ast) { + let value = this.value; + + ast.traverse((node) => { + if (!node.is('selector') && !node.is('arguments')) return; + + node.forEach('typeSelector', (selector) => { + selector.forEach('ident', (ident) => { + ident.content = value === 'lower' ? + ident.content.toLowerCase() : + ident.content.toUpperCase(); + }); + }); + }); + }, + + /** + * Detects the value of this option in ast. + * @param {Node} ast + * @return {Array} List of detected values + */ + detect(ast) { + let detected = []; + + ast.traverse((node) => { + if (!node.is('selector') && !node.is('arguments')) return; + + node.forEach('typeSelector', (selector) => { + selector.forEach('ident', (ident) => { + if (ident.content.match(/^[a-z]+$/)) { + detected.push('lower'); + } else if (ident.content.match(/^[A-Z]+$/)) { + detected.push('upper'); + } + }); + }); + }); + + return detected; + } +}; + +module.exports = option; diff --git a/src/options/eof-newline.js b/src/options/eof-newline.js new file mode 100644 index 00000000..29196cd4 --- /dev/null +++ b/src/options/eof-newline.js @@ -0,0 +1,64 @@ +'use strict'; + +let gonzales = require('gonzales-pe'); + +let option = { + /** + * Option's name as it's used in config. + * @type {String} + */ + get name() { + return 'eof-newline'; + }, + + /** + * List of syntaxes that are supported by this option. + * @type {Array} + */ + get syntax() { + return ['css', 'less', 'sass', 'scss']; + }, + + /** + * Types of values this option accepts in config. + * @type {Object} + */ + get accepts() { + return { + boolean: [true, false] + }; + }, + + /** + * Processes ast and fixes found code style errors. + * @param {Node} ast + */ + process(ast) { + var lastChild = ast.last(); + + if (!lastChild.is('space')) { + lastChild = gonzales.createNode({type: 'space', content: ''}); + ast.content.push(lastChild); + } + + lastChild.content = lastChild.content.replace(/\n$/, ''); + if (this.value) lastChild.content += '\n'; + }, + + /** + * Detects the value of this option in ast. + * @param {Node} ast + * @return {Array} List of detected values + */ + detect(ast) { + var lastChild = ast.last(); + + if (lastChild.is('space') && lastChild.content.indexOf('\n') !== -1) { + return [true]; + } else { + return [false]; + } + } +}; + +module.exports = option; diff --git a/src/options/leading-zero.js b/src/options/leading-zero.js new file mode 100644 index 00000000..77381c39 --- /dev/null +++ b/src/options/leading-zero.js @@ -0,0 +1,46 @@ +'use strict'; + +module.exports = { + name: 'leading-zero', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + boolean: [true, false] + }, + + /** + * Processes tree node. + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('number', function(number) { + if (!value) { + number.content = number.content.replace(/^0+(?=\.)/, ''); + } else if (number.content[0] === '.') { + number.content = '0' + number.content; + } + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByType('number', function(number) { + if (number.content.match(/^\.[0-9]+/)) { + detected.push(false); + } else if (number.content.match(/^0\.[0-9]+/)) { + detected.push(true); + } + }); + + return detected; + } +}; diff --git a/src/options/lines-between-rulesets.js b/src/options/lines-between-rulesets.js new file mode 100644 index 00000000..15d99203 --- /dev/null +++ b/src/options/lines-between-rulesets.js @@ -0,0 +1,250 @@ +'use strict'; + +let gonzales = require('gonzales-pe'); + +let option = { + newLinesString: '', + newLinesNode: null, + + /** + * Option's name as it's used in config. + * @type {String} + */ + get name() { + return 'lines-between-rulesets'; + }, + + /** + * Name of option that must run after this option. + * @type {String} + */ + get runBefore() { + return 'block-indent'; + }, + + /** + * List of syntaxes that are supported by this option. + * @type {Array} + */ + get syntax() { + return ['css', 'less', 'sass', 'scss']; + }, + + /** + * Types of values this option accepts in config. + * @type {Object} + */ + get accepts() { + return { + number: true + }; + }, + + /** + * @param {number} value + * @returns {number} + */ + /* + ** Still need to override, as the core implementation of setValue doesn't + ** pass numbers through, but creates a string of spaces of the same length. + */ + setValue(value) { + let valueType = typeof value; + + if (valueType !== 'number') { + throw new Error('Value must be a number.'); + } + + return value; + }, + + buildSpacing(syntax) { + let spacing = ''; + let numNewLines = 0; + let newLinesOffset = 1; + + if (syntax === 'sass') { + newLinesOffset = 0; + } + + numNewLines = Math.round(this.value) + newLinesOffset; + + for (var i = 0; i < numNewLines; i++) { + spacing += '\n'; + } + + return spacing; + }, + + /** + * Processes ast and fixes found code style errors. + * @param {Node} ast + */ + process(ast) { + this.newLinesString = this.buildSpacing(ast.syntax); + this.newLinesNode = gonzales.createNode({ + type: 'space', + content: this.newLinesString + }); + this.processBlock(ast); + }, + + processBlock(x) { + if (x.is('stylesheet')) { + // Check all @rules + this.processAtRules(x); + + // Check all rulesets + this.processRuleSets(x); + } + + x.forEach((node) => { + if (!node.is('block')) { + return this.processBlock(node); + } + + // Check all @rules + this.processAtRules(node); + + // Check all rulesets + this.processRuleSets(node); + + this.processBlock(node); + }); + }, + + processAtRules(node) { + node.forEach('atrule', (atRuleNode, index) => { + this.insertNewlines(node, index); + }); + }, + + processRuleSets(node) { + node.forEach('ruleset', (ruleSetNode, index) => { + this.insertNewlines(node, index); + }); + }, + + isComment(node) { + if (!node) { + return false; + } + return (node.is('singlelineComment') || node.is('multilineComment')); + }, + + isNewline(node) { + if (!node) { + return false; + } + return (node.content === '\n'); + }, + + prevLineIsComment(parent, index) { + let indexThreshold = 2; + let prevChild; + let prevMinusOneChild; + let prevMinusTwoChild; + let parentSyntax = parent ? parent.syntax : null; + + // Sass is troublesome because newlines are counted as separate nodes + if (parentSyntax === 'sass') { + indexThreshold = 3; + } + + if (!parent || index < indexThreshold) { + return false; + } + + prevChild = parent.get(index - 1); + prevMinusOneChild = parent.get(index - 2); + + if (parentSyntax === 'sass') { + prevMinusTwoChild = parent.get(index - 3); + return ( + this.isComment(prevMinusTwoChild) && + this.isNewline(prevMinusOneChild) && + prevChild.is('space') + ); + } + + return (this.isComment(prevMinusOneChild) && prevChild.is('space')); + }, + + /* + ** Find the latest previous child that isn't a comment, and return its index. + */ + findLatestNonCommentNode(parent, index) { + let prevChild; + let lastNonCommentIndex = -1; + let currentIndex = index; + let jumpSize = 2; + + if (parent.syntax === 'sass') { + jumpSize = 3; + } + + while (currentIndex >= 0) { + if (this.prevLineIsComment(parent, currentIndex)) { + currentIndex -= jumpSize; + continue; + } + + prevChild = parent.get(currentIndex - 1); + + if (!this.isComment(prevChild)) { + lastNonCommentIndex = currentIndex - 1; + break; + } + + currentIndex--; + } + + return lastNonCommentIndex; + }, + + insertNewlinesAsString(node) { + let content = node.content; + let lastNewline = content.lastIndexOf('\n'); + let newContent; + + if (lastNewline > -1) { + content = content.substring(lastNewline + 1); + } + + newContent = this.newLinesString + content; + node.content = newContent; + }, + + insertNewlinesAsNode(node) { + node.insert(node.length, this.newLinesNode); + }, + + insertNewlines(node, index) { + let prevChild = node.get(index - 1); + let shouldInsert = false; + + // Check for previous nodes that are not a space + // Do not insert if the ruleset is the first item + for (var i = 0; i < index; i++) { + if (!node.get(i).is('space')) { + shouldInsert = true; + break; + } + } + + if (prevChild && shouldInsert) { + if (this.prevLineIsComment(node, index) || this.isComment(prevChild)) { + let lastNonCommentIndex = this.findLatestNonCommentNode(node, index); + prevChild = node.get(lastNonCommentIndex); + } + + if (prevChild.is('space')) { + this.insertNewlinesAsString(prevChild); + } else { + this.insertNewlinesAsNode(prevChild); + } + } + } +}; + +module.exports = option; diff --git a/src/options/quotes.js b/src/options/quotes.js new file mode 100644 index 00000000..3e591783 --- /dev/null +++ b/src/options/quotes.js @@ -0,0 +1,58 @@ +'use strict'; + +module.exports = { + name: 'quotes', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + string: /^single|double$/ + }, + + /** + * Processes tree node. + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('string', function(string) { + if (string.content[0] === '"' && value === 'single') { + string.content = string.content + // Unescape all escaped double quotes + .replace(/\\"/g, '"') + // Escape all the single quotes + .replace(/([^\\])'/g, '$1\\\'') + // Replace the first and the last quote + .replace(/^"|"$/g, '\''); + } else if (string.content[0] === '\'' && value === 'double') { + string.content = string.content + // Unescape all escaped single quotes + .replace(/\\'/g, '\'') + // Escape all the double quotes + .replace(/([^\\])"/g, '$1\\\"') + // Replace the first and the last quote + .replace(/^'|'$/g, '"'); + } + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByType('string', function(string) { + if (string.content[0] === '"') { + detected.push('double'); + } else if (string.content[0] === '\'') { + detected.push('single'); + } + }); + + return detected; + } +}; diff --git a/src/options/remove-empty-rulesets.js b/src/options/remove-empty-rulesets.js new file mode 100644 index 00000000..faafd51f --- /dev/null +++ b/src/options/remove-empty-rulesets.js @@ -0,0 +1,91 @@ +'use strict'; + +module.exports = (function() { + function processNode(node) { + removeEmptyRulesets(node); + mergeAdjacentWhitespace(node); + } + + function removeEmptyRulesets(stylesheet) { + stylesheet.forEach('ruleset', function(ruleset, i) { + var block = ruleset.first('block'); + processNode(block); + if (isEmptyBlock(block)) stylesheet.removeChild(i); + }); + } + + /** + * Removing ruleset nodes from tree may result in two adjacent whitespace + * nodes which is not correct AST: + * [space, ruleset, space] => [space, space] + * To ensure correctness of further processing we should merge such nodes + * into one: + * [space, space] => [space] + */ + function mergeAdjacentWhitespace(node) { + var i = node.content.length - 1; + while (i-- > 0) { + if (node.get(i).is('space') && + node.get(i + 1) && node.get(i + 1).is('space')) { + node.get(i).content += node.get(i + 1).content; + node.removeChild(i + 1); + } + } + } + + /** + * Block is considered empty when it has nothing but spaces. + */ + function isEmptyBlock(node) { + if (!node.length) return true; + + return !node.content.some(function(node) { + return !node.is('space'); + }); + } + + return { + name: 'remove-empty-rulesets', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + boolean: [true] + }, + + /** + * Remove rulesets with no declarations. + * + * @param {String} ast + */ + process: function(ast) { + processNode(ast); + }, + + detectDefault: true, + + /** + * Detects the value of an option at the tree node. + * This option is treated as `true` by default, but any trailing space + * would invalidate it. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverse(function(node) { + if (!node.is('atrulers') && !node.is('block')) return; + + if (node.length === 0 || + (node.length === 1 && node.first().is('space'))) { + detected.push(false); + } + }); + + return detected; + } + }; +})(); diff --git a/src/options/sort-order-fallback.js b/src/options/sort-order-fallback.js new file mode 100644 index 00000000..318094db --- /dev/null +++ b/src/options/sort-order-fallback.js @@ -0,0 +1,13 @@ +'use strict'; + +module.exports = { + name: 'sort-order-fallback', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: {string: /^abc$/}, + + process: function() {}, + + detect: () => [] +}; diff --git a/src/options/sort-order.js b/src/options/sort-order.js new file mode 100644 index 00000000..27a2198a --- /dev/null +++ b/src/options/sort-order.js @@ -0,0 +1,432 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = { + get name() { + return 'sort-order'; + }, + + get runBefore() { + return 'space-before-closing-brace'; + }, + + get syntax() { + return ['css', 'less', 'sass', 'scss']; + }, + + /** + * @param {Array} value Option value + * @returns {Array} + */ + setValue(value) { + if (!Array.isArray(value)) + throw new Error('The option accepts only array of properties.'); + + var order = {}; + + if (typeof value[0] === 'string') { + // If there is only one group of properties. + value.forEach(function(prop, propIndex) { + order[prop] = {group: 0, prop: propIndex}; + }); + } else { + value.forEach(function(group, groupIndex) { + group.forEach(function(prop, propIndex) { + order[prop] = {group: groupIndex, prop: propIndex}; + }); + }); + } + + return order; + }, + + /** + * @param {node} ast + * @param {object} config + */ + process(ast, config) { + this._config = config; + // Sort properties only inside blocks. + ast.traverseByType('block', this._processBlock.bind(this)); + }, + + _cleanSassLinebreaks(node) { + let containsOnlyLinebreaks = true; + + node.forEach((space) => { + if (!space.is('space') || space.content !== '\n') { + containsOnlyLinebreaks = false; + return null; + } + }); + + if (containsOnlyLinebreaks) + node.content = []; + }, + + _extendNode(block, i, spacesBefore) { + let nodesToDelete = [i]; + let node = block.get(i); + let extendedNode = {i: i, node: node}; + + let propertyName = this._getSortableName(node); + if (!propertyName) return null; + + // Check if current node's property name is in sort order. + let propertyIndex = this.value[propertyName]; + // If the declaration's property is in order's list, save its + // group and property indices. Otherwise set them to 10000, so + // declaration appears at the bottom of a sorted list: + extendedNode.groupIndex = propertyIndex && propertyIndex.group > -1 ? + propertyIndex.group : this._getLastGroupIndex(); + extendedNode.propertyIndex = propertyIndex && propertyIndex.prop > -1 ? + propertyIndex.prop : this._getLastPropertyIndex(); + + // Spaces before node. + nodesToDelete = nodesToDelete.concat(spacesBefore); + extendedNode.spacesBeforeNode = + this._getNodesByIndex(block, spacesBefore); + + // Spaces after node. + let spacesBeforeDelimiter = + this._getSpacesAndCommentsAfterNode(block, i); + nodesToDelete = nodesToDelete.concat(spacesBeforeDelimiter); + extendedNode.spacesBeforeDelimiter = + this._getNodesByIndex(block, spacesBeforeDelimiter); + + i += spacesBeforeDelimiter.length; + + // Spaces after delimiter. + // If there is `;` right after the declaration, save it with the + // declaration and mark it for removing from parent node: + if (block.get(i + 1) && block.get(i + 1).is('declarationDelimiter')) { + i += 1; + node = block.get(i); + nodesToDelete.push(i); + extendedNode.delim = node; + + if (node.syntax !== 'sass') { + // Save spaces and comments which follow right after + // the declaration and mark them for removing from parent node: + let spacesAfterDelimiter = + this._getSpacesAndCommentsAfterNode(block, i); + i += spacesAfterDelimiter.length; + nodesToDelete = nodesToDelete.concat(spacesAfterDelimiter); + extendedNode.spacesAfterDelimiter = + this._getNodesByIndex(block, spacesAfterDelimiter); + } + } + + extendedNode.endIndex = i; + // Remove all nodes, that were moved to `sortables` list, + // from block node: + extendedNode.nodesToDelete = nodesToDelete; + + return extendedNode; + }, + + _getLastGroupIndex() { + return this.value && this.value['...'] ? + this.value['...'].group : Infinity; + }, + + _getLastPropertyIndex() { + return this.value && this.value['...'] ? + this.value['...'].prop : Infinity; + }, + + _getNodesByIndex(block, index) { + return index.map((i) => block.get(i)); + }, + + _getSortableIncludeName(node) { + // Divide `include` into mixins with specific name + // (e. g. `$include breakpoint`), and the rest — `$include`. + let mixinName; + + if (node.syntax === 'less') { + // `node.first()` is class and `node.first().first()` is ident. + mixinName = node.first().first().content; + } else if (node.syntax === 'sass' && node.first().content === '+') { + // `node.first()` is `+` and `node.get(1)` is ident. + mixinName = node.get(1).content; + } else { + // `node.first()` is @-keyword, `node.get(1)` is space and + // `node.get(2)` is ident. + mixinName = node.get(2).content; + } + + let includeMixinName = '$include ' + mixinName; + return this.value.hasOwnProperty(includeMixinName) ? + includeMixinName : + '$include'; + }, + + _getSortableName(node) { + if (node.is('extend')) return '$extend'; + if (node.is('include')) return this._getSortableIncludeName(node); + else return this._getSortablePropertyName(node); + }, + + _getSortablePropertyName(node) { + if (node.is('declaration')) { + let property = node.first('property').first(); + return property.is('variable') ? '$variable' : property.content; + } + + let atkeyword = node.first('atkeyword'); + if (atkeyword && atkeyword.first().content === 'import') + return '$import'; + }, + + _getSpacesAndCommentsAfterNode(node, i) { + // List of start positions for nodes with spaces and comments: + let positions = []; + + // Skip node itself. + i++; + + for (let l = node.length; i < l; i++) { + let currentNode = node.get(i); + + // If node is nor spaces neither comment, stop. + if (!this._isSpaceOrComment(currentNode)) break; + + if (currentNode.is('multilineComment') || + currentNode.is('singlelineComment')) { + positions.push(i); + continue; + } + + // If there are any line breaks in a node with spaces, stop and + // split the node into two: one with spaces before line break + // and one with `\n` symbol and everything that goes after. + // Combine the first one with declaration/@-rule's node: + let linebreakIndex = currentNode.content.indexOf('\n'); + if (linebreakIndex !== -1) { + var s = currentNode.content.substring(0, linebreakIndex); + if (s === '') break; + var space = gonzales.createNode({type: 'space', content: s}); + node.insert(i + 1, space); + positions.push(i + 1); + currentNode.content = currentNode.content + .substring(linebreakIndex); + break; + } + + positions.push(i); + } + + return positions; + }, + + /** + * Check if there are any comments or spaces before + * the declaration/@-rule. + * @param {Node} node + * @param {Number} i + * @returns {Array} List of nodes with spaces and comments + */ + _getSpacesAndCommentsBeforeNode(node, i) { + // List of start positions for nodes with spaces and comments: + let positions = []; + let sendPositions = false; + + for (let l = node.length; i < l; i++) { + let currentNode = node.get(i); + + // If the node is declaration or @-rule, stop and return all + // found nodes with spaces and comments (if there are any): + if (!this._isSpaceOrComment(currentNode)) { + sendPositions = true; + break; + } + + positions.push(i); + } + + return sendPositions ? positions : null; + }, + + _insertSortablesToBlock(nodesToSort, node) { + if (node.syntax === 'sass') + this._cleanSassLinebreaks(node); + + for (let i = nodesToSort.length - 1, l = -1; i > l; i--) { + let currentNode = nodesToSort[i]; + let prevNode = nodesToSort[i - 1]; + let spacesBeforeNode = currentNode.spacesBeforeNode || []; + let spacesBeforeDelimiter = currentNode.spacesBeforeDelimiter || []; + let spacesAfterDelimiter = currentNode.spacesAfterDelimiter || []; + + if (node.syntax === 'sass' && spacesBeforeNode.length) { + let space = spacesBeforeNode[0]; + space.content = space.content.replace(/\n/, ''); + } + + spacesBeforeNode.reverse().map(this._removeEmptyLines); + spacesBeforeDelimiter.reverse().map(this._removeEmptyLines); + spacesAfterDelimiter.reverse().map(this._removeEmptyLines); + + // Divide declarations from different groups with + // an empty line: + if (prevNode && currentNode.groupIndex > prevNode.groupIndex) { + let space = spacesBeforeNode[0]; + if (space && space.is('space') && + (space.syntax === 'sass' || + space.content.match(/\n/g) && + space.content.match(/\n/g).length < 2)) { + space.content = '\n' + space.content; + } + } + + for (let j = 0, nl = spacesAfterDelimiter.length; j < nl; j++) { + node.content.unshift(spacesAfterDelimiter[j]); + } + + if (currentNode.delim) { + node.content.unshift(currentNode.delim); + } else if (i !== nodesToSort.length - 1 && + (currentNode.node.is('declaration') || + currentNode.node.is('extend'))) { + let delimiter = gonzales.createNode({ + type: 'declarationDelimiter', + content: currentNode.node.syntax === 'sass' ? '\n' : ';' + }); + node.content.unshift(delimiter); + } + + for (let j = 0, nl = spacesBeforeDelimiter.length; j < nl; j++) { + node.content.unshift(spacesBeforeDelimiter[j]); + } + + node.content.unshift(currentNode.node); + + for (let j = 0, nl = spacesBeforeNode.length; j < nl; j++) { + node.content.unshift(spacesBeforeNode[j]); + } + } + }, + + // Types of nodes that can be sorted. + _isAcceptableNode(node) { + const NODES = ['atrule', 'declaration', 'extend', 'include', + 'multilineComment', 'singlelineComment', 'space']; + return NODES.indexOf(node.type) !== -1; + }, + + // Spaces and comments. + _isSpaceOrComment(node) { + const SC = ['multilineComment', 'singlelineComment', 'space']; + return SC.indexOf(node.type) !== -1; + }, + + _processBlock(block) { + // Check every child node. + // If it is declaration (property-value pair, e.g. `color: tomato`), + // or @-rule (e.g. `@include nani`), + // combine it with spaces, semicolon and comments and move them from + // current node to a separate list for further sorting: + let nodesToSort = this._separateSortablesFromBlock(block); + this._sortNodes(nodesToSort); + this._insertSortablesToBlock(nodesToSort, block); + }, + + /** + * Remove empty lines in space node. + * @param {node} node Space node. + */ + _removeEmptyLines(node) { + node.content = node.content.replace(/\n[\s\t\n\r]*\n/, '\n'); + }, + + _separateSortablesFromBlock(block) { + let sortables = []; + let nodesToDelete = []; + + // Don't cache `block.length` since we may insert new nodes into it. + for (let i = 0; i < block.length; i++) { + let node = block.get(i); + if (!this._isAcceptableNode(node)) continue; + + // Save preceding spaces and comments, if there are any, + // and mark them for removing from parent node: + let spacesBeforeNode = + this._getSpacesAndCommentsBeforeNode(block, i); + if (!spacesBeforeNode) break; + + i += spacesBeforeNode.length; + node = block.get(i); + + let extendedNode = this._extendNode(block, i, spacesBeforeNode); + if (!extendedNode) continue; + + nodesToDelete = nodesToDelete.concat(extendedNode.nodesToDelete); + i = extendedNode.endIndex; + sortables.push(extendedNode); + } + + nodesToDelete.sort((a, b) => a - b); + for (let x = nodesToDelete.length - 1; x > -1; x--) + block.removeChild(nodesToDelete[x]); + + return sortables; + }, + + _sortLeftovers(a, b) { + let prefixes = ['-webkit-', '-moz-', '-ms-', '-o-', '']; + let prefixesRegExp = /^(-webkit-|-moz-|-ms-|-o-)(.*)$/; + + // Get property name (i.e. `color`, `-o-animation`): + a = a.node.first().first().content; + b = b.node.first().first().content; + + // Get prefix and unprefixed part. For example: + // ['-o-animation', '-o-', 'animation'] + // ['color', '', 'color'] + a = a.match(prefixesRegExp) || [a, '', a]; + b = b.match(prefixesRegExp) || [b, '', b]; + + if (a[2] !== b[2]) { + // If unprefixed parts are different (i.e. `border` and + // `color`), compare them: + return a[2] <= b[2] ? -1 : 1; + } else { + // If unprefixed parts are identical (i.e. `border` in + // `-moz-border` and `-o-border`), compare prefixes. + // They should go in the same order they are set + // in `prefixes` array. + return prefixes.indexOf(a[1]) <= prefixes.indexOf(b[1]) ? -1 : 1; + } + }, + + _sortNodes(nodes) { + nodes.sort((a, b) => { + // If a's group index is higher than b's group index, in + // a sorted list a appears after b: + if (a.groupIndex !== b.groupIndex) + return a.groupIndex - b.groupIndex; + + // If a and b belong to leftovers and `sort-order-fallback` + // option is set to `abc`, sort properties alphabetically: + if (a.groupIndex === this._getLastGroupIndex() && + this._config['sort-order-fallback']) { + return this._sortLeftovers(a, b); + } + + // If a and b have the same group index, and a's property index + // is higher than b's property index, in a sorted list + // a appears after b: + if (a.propertyIndex !== b.propertyIndex) + return a.propertyIndex - b.propertyIndex; + + // If a and b have the same group index and the same property + // index, in a sorted list they appear in the same order + // they were in original array: + return a.i - b.i; + }); + }, + + detect: () => [] +}; diff --git a/src/options/space-after-colon.js b/src/options/space-after-colon.js new file mode 100644 index 00000000..bd3007fb --- /dev/null +++ b/src/options/space-after-colon.js @@ -0,0 +1,65 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = { + name: 'space-after-colon', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('propertyDelimiter', function(delimiter, i, parent) { + if (delimiter.syntax === 'sass' && !parent.get(i - 1)) + return null; + + // Remove any spaces after colon: + if (parent.get(i + 1) && parent.get(i + 1).is('space')) + parent.removeChild(i + 1); + // If the value set in config is not empty, add spaces: + if (value !== '') { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + parent.insert(i + 1, space); + } + + return null; + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByType('propertyDelimiter', function(delimiter, i, parent) { + var nextNode = parent.get(i + 1); + + if (nextNode.is('space')) { + detected.push(nextNode.content); + } else { + detected.push(''); + } + }); + + return detected; + } +}; diff --git a/src/options/space-after-combinator.js b/src/options/space-after-combinator.js new file mode 100644 index 00000000..556204b4 --- /dev/null +++ b/src/options/space-after-combinator.js @@ -0,0 +1,60 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = { + name: 'space-after-combinator', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('combinator', function(combinator, i, parent) { + var nextNode = parent.get(i + 1); + + if (nextNode && nextNode.is('space')) { + nextNode.content = value; + } else { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + parent.insert(i + 1, space); + } + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByType('combinator', function(combinator, i, parent) { + var nextNode = parent.get(i + 1); + + if (nextNode.is('space')) { + detected.push(nextNode.content); + } else { + detected.push(''); + } + }); + + return detected; + } +}; diff --git a/src/options/space-after-opening-brace.js b/src/options/space-after-opening-brace.js new file mode 100644 index 00000000..ed04b1f3 --- /dev/null +++ b/src/options/space-after-opening-brace.js @@ -0,0 +1,62 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = { + name: 'space-after-opening-brace', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverse(function(node) { + // If found block node stop at the next one for space check + if (!node.is('block') && !node.is('atrulers')) return; + + if (node.first() && + node.first().is('space')) { + node.first().content = value; + } else if (value !== '') { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + node.insert(0, space); + } + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverse(function(node) { + if (!node.is('block') && !node.is('atrulers')) return; + + if (node.first().is('space')) { + detected.push(node.first().content); + } else { + detected.push(''); + } + }); + + return detected; + } +}; diff --git a/src/options/space-after-selector-delimiter.js b/src/options/space-after-selector-delimiter.js new file mode 100644 index 00000000..995ed373 --- /dev/null +++ b/src/options/space-after-selector-delimiter.js @@ -0,0 +1,69 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = { + name: 'space-after-selector-delimiter', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('delimiter', function(delimiter, i, parent) { + if (parent.is('arguments')) return; + + var nextNode = parent.get(i + 1); + if (!nextNode) return; + + if (nextNode.is('space')) { + nextNode.content = value; + } else if (nextNode.first().is('space')) { + nextNode.first().content = value; + } else { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + nextNode.insert(0, space); + } + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByType('delimiter', function(delimiter, i, parent) { + if (parent.is('arguments')) return; + + var nextNode = parent.get(i + 1); + + if (nextNode && nextNode.is('space')) { + detected.push(nextNode.content); + } else if (nextNode.first() && nextNode.first().is('space')) { + detected.push(nextNode.first().content); + } else { + detected.push(''); + } + }); + + return detected; + } +}; diff --git a/src/options/space-before-closing-brace.js b/src/options/space-before-closing-brace.js new file mode 100644 index 00000000..1cd276e2 --- /dev/null +++ b/src/options/space-before-closing-brace.js @@ -0,0 +1,104 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = (function() { + var valueFromSettings; + var blockIndent; + + function getLastWhitespaceNode(node) { + var lastNode = node.last(); + + if (!lastNode || !lastNode.content) return null; + + if (lastNode.is('block')) return null; + if (lastNode.is('space')) return lastNode; + + return getLastWhitespaceNode(lastNode); + } + + function processBlock(x, level) { + level = level || 0; + + x.forEach(function(node) { + if (!node.is('block') && + !node.is('atrulers')) return processBlock(node, level); + + level++; + + var value = valueFromSettings; + if (value.indexOf('\n') > -1) { + // TODO: Check that it works for '' block indent value + if (blockIndent) { + value += new Array(level).join(blockIndent); + } + } + + // If found block node stop at the next one for space check + // For the pre-block node, find its last (the deepest) child + var whitespaceNode = getLastWhitespaceNode(node); + + // If it's spaces, modify this node + // If it's something different from spaces, add a space node + // to the end + if (whitespaceNode) { + whitespaceNode.content = value; + } else if (value !== '') { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + node.content.push(space); + } + + processBlock(node, level); + }); + } + + + return { + name: 'space-before-closing-brace', + + runBefore: 'tab-size', + + syntax: ['css', 'less', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * @param {node} ast + * @param {Object} config + */ + process: function(ast, config) { + valueFromSettings = this.value; + blockIndent = config['block-indent']; + + processBlock(ast); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByTypes(['block', 'atrulers'], function(node) { + // For the block node, find its last (the deepest) child + var whitespaceNode = getLastWhitespaceNode(node); + if (whitespaceNode) { + detected.push(whitespaceNode.content); + } else { + detected.push(''); + } + }); + + return detected; + } + }; +})(); diff --git a/src/options/space-before-colon.js b/src/options/space-before-colon.js new file mode 100644 index 00000000..1490bcb3 --- /dev/null +++ b/src/options/space-before-colon.js @@ -0,0 +1,65 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = { + name: 'space-before-colon', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('propertyDelimiter', function(delimiter, i, parent) { + if (delimiter.syntax === 'sass' && !parent.get(i - 1)) + return; + + // Remove any spaces before colon: + if (parent.get(i - 1).is('space')) { + parent.removeChild(--i); + } + + // If the value set in config is not empty, add spaces: + if (value !== '') { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + parent.insert(i, space); + } + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByType('propertyDelimiter', function(delimiter, i, parent) { + var previousNode = parent.get(i - 1); + + if (previousNode && previousNode.is('space')) { + detected.push(previousNode.content); + } else { + detected.push(''); + } + }); + + return detected; + } +}; diff --git a/src/options/space-before-combinator.js b/src/options/space-before-combinator.js new file mode 100644 index 00000000..6360e6ea --- /dev/null +++ b/src/options/space-before-combinator.js @@ -0,0 +1,62 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = { + name: 'space-before-combinator', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('combinator', function(combinator, i, parent) { + var previousNode = parent.get(i - 1); + + if (!previousNode) return; + + if (previousNode.is('space')) { + previousNode.content = value; + } else { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + parent.insert(i, space); + } + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByType('combinator', function(combinator, i, parent) { + var previousNode = parent.get(i - 1); + + if (previousNode && previousNode.is('space')) { + detected.push(previousNode.content); + } else { + detected.push(''); + } + }); + + return detected; + } +}; diff --git a/src/options/space-before-opening-brace.js b/src/options/space-before-opening-brace.js new file mode 100644 index 00000000..e3eca162 --- /dev/null +++ b/src/options/space-before-opening-brace.js @@ -0,0 +1,75 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = (function() { + return { + name: 'space-before-opening-brace', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + // If found block node stop at the next one for space check. + ast.traverseByTypes(['block', 'value'], function(block, i, parent) { + if (block.is('value') && !block.first().is('block')) return; + + var previousNode = parent.get(i - 1); + if (!previousNode) return; + + // If it's spaces, modify this node. + // If it's something different from spaces, add a space node to + // the end: + if (previousNode.is('space')) { + previousNode.content = value; + } else if (value !== '') { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + parent.insert(i, space); + } + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + var detected = []; + + ast.traverseByTypes(['block', 'value'], function(block, i, parent) { + if (block.is('value') && !block.first().is('block')) return; + + var previousNode = parent.get(i - 1); + if (!previousNode) return; + + // If it's spaces, modify this node. + // If it's something different from spaces, add a space node to + // the end: + if (previousNode.is('space')) { + detected.push(previousNode.content); + } else { + detected.push(''); + } + }); + + return detected; + } + }; +})(); diff --git a/src/options/space-before-selector-delimiter.js b/src/options/space-before-selector-delimiter.js new file mode 100644 index 00000000..29f153e2 --- /dev/null +++ b/src/options/space-before-selector-delimiter.js @@ -0,0 +1,64 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = { + name: 'space-before-selector-delimiter', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('delimiter', function(delimiter, i, parent) { + if (parent.is('arguments')) return; + + var previousNode = parent.get(i - 1); + + if (previousNode && previousNode.is('space')) { + previousNode.content = value; + } else { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + parent.insert(i, space); + } + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByType('delimiter', function(delimiter, i, parent) { + if (parent.is('arguments')) return; + + var previousNode = parent.get(i - 1); + + if (previousNode && previousNode.is('space')) { + detected.push(previousNode.content); + } else { + detected.push(''); + } + }); + + return detected; + } +}; diff --git a/src/options/space-between-declarations.js b/src/options/space-between-declarations.js new file mode 100644 index 00000000..2c14e60c --- /dev/null +++ b/src/options/space-between-declarations.js @@ -0,0 +1,97 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = (function() { + function getDeclarationEnd(node, i) { + for (; i < node.length; i++) { + if (!node.get(i + 1) || typeof node.get(i + 1) === 'string') { + return 0; + } else if (node.get(i + 1).is('space')) { + if (node.get(i + 1).content.indexOf('\n') > -1) { + if (node.get(i + 2) && node.get(i + 2).is('declaration')) { + return i; + } else { + return 0; + } + } else if (node.get(i + 2) && + node.get(i + 2).is('multilineComment')) { + if (node.get(i + 3) && node.get(i + 3).is('declaration')) { + return i + 2; + } else if (node.get(i + 3) && node.get(i + 3).is('space')) { + if (node.get(i + 4) && + node.get(i + 4).is('declaration')) { + return i + 2; + } else { + return 0; + } + } else { + return 0; + } + } else if (node.get(i + 2) && + node.get(i + 2).is('declaration')) { + return i; + } + } else if (node.get(i + 1).is('declaration')) { + return i; + } else if (node.get(i + 1).is('multilineComment')) { + if (node.get(i + 2) && node.get(i + 2).is('declaration')) { + return i + 1; + } else if (node.get(i + 2) && node.get(i + 2).is('space')) { + if (node.get(i + 3) && node.get(i + 3).is('declaration')) { + return i + 1; + } + } else { + return 0; + } + } else { + return 0; + } + } + } + + return { + name: 'space-between-declarations', + + runBefore: 'block-indent', + + syntax: ['css', 'less', 'scss'], + + accepts: { + number: true, + string: /^[ \t\n]*$/ + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('declarationDelimiter', (delimiter, i, parent) => { + // Grom user's point of view "declaration" includes semicolons + // and comments placed on the same line. + // So group those things together: + var declarationEnd = getDeclarationEnd(parent, i); + if (!declarationEnd) { + return; + } else { + i = declarationEnd; + } + + var nextNode = parent.get(i + 1); + if (nextNode && nextNode.is('space')) { + nextNode.content = value; + } else { + var space = gonzales.createNode({ + type: 'space', + content: value + }); + parent.insert(i + 1, space); + } + }); + } + }; +})(); diff --git a/src/options/strip-spaces.js b/src/options/strip-spaces.js new file mode 100644 index 00000000..50ea4ae9 --- /dev/null +++ b/src/options/strip-spaces.js @@ -0,0 +1,66 @@ +'use strict'; + +module.exports = (function() { + /** + * Trim trailing spaces on each line. + * @private + * @param {String} string Spaceful string + * @returns {String} + */ + function trim(string) { + return string.replace(/[ \t]+\n/g, '\n'); + } + + return { + name: 'strip-spaces', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + boolean: [true] + }, + + /** + * Processes tree node. + * @param {node} ast + */ + process: function(ast) { + var lastChild = ast.last(); + if (lastChild.is('space')) { + lastChild.content = trim(lastChild.content) + .replace(/[ \t]+$/, '') + .replace(/[\n]+/g, '\n'); + } + + ast.traverseByType('space', function(space) { + space.content = trim(space.content); + }); + }, + + detectDefault: true, + + /** + * Detects the value of an option at the tree node. + * This option is treated as `true` by default, but any trailing + * space would invalidate it. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + var lastChild = ast.last(); + if (lastChild.is('space') && + lastChild.content !== '\n' && + lastChild.content.match(/^[ \n\t]+$/)) { + detected.push(false); + } + + ast.traverseByType('space', function(space) { + if (space.content.match(/[ \t]\n/)) detected.push(false); + }); + + return detected; + } + }; +})(); diff --git a/src/options/tab-size.js b/src/options/tab-size.js new file mode 100644 index 00000000..d9a4b753 --- /dev/null +++ b/src/options/tab-size.js @@ -0,0 +1,26 @@ +'use strict'; + +module.exports = { + name: 'tab-size', + + runBefore: 'vendor-prefix-align', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + number: true + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + let value = this.value; + + ast.traverseByType('space', function(space) { + space.content = space.content.replace(/\t/, value); + }); + } +}; diff --git a/src/options/unitless-zero.js b/src/options/unitless-zero.js new file mode 100644 index 00000000..467db682 --- /dev/null +++ b/src/options/unitless-zero.js @@ -0,0 +1,77 @@ +'use strict'; + +module.exports = { + name: 'unitless-zero', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + boolean: [true] + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + var UNITS = ['cm', 'em', 'ex', 'pt', 'px']; + + ast.traverseByTypes(['value', 'parentheses'], function(node) { + node.forEach(function(value) { + if (typeof value === 'string') return; + + if (value.is('dimension')) { + var unit = value.first('ident').content; + if (value.first('number').content === '0' && + UNITS.indexOf(unit) !== -1) { + value.removeChild(1); + } + } else if (value.is('percentage')) { + var number = value.first('number').content; + if (number === '0') { + value.type = 'number'; + value.content = number; + } + } + }); + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverse(function(node, index, parent) { + // If we see a zero with unit and it is not degree, + // then we don’t have an option + if (node.is('percentage') && + node.first('number').content[1] === '0') { + detected.push(false); + return; + } + + if (node.is('dimension') && + node.first('number').content === '0' && + node.first('ident').content !== 'deg') { + detected.push(false); + return; + } + + // If we see a zero and previous node is not percentage + // or dimension, then we have an option + if (node.is('number') && + node.content === '0' && + !parent.is('percentage') && + !parent.is('dimension')) { + detected.push(true); + } + }); + + return detected; + } +}; diff --git a/src/options/vendor-prefix-align.js b/src/options/vendor-prefix-align.js new file mode 100644 index 00000000..a816340e --- /dev/null +++ b/src/options/vendor-prefix-align.js @@ -0,0 +1,474 @@ +'use strict'; + +var gonzales = require('gonzales-pe'); + +module.exports = (function() { + // Vendor prefixes list: + var PREFIXES = [ + 'webkit', + 'khtml', + 'moz', + 'ms', + 'o' + ]; + + var oneline; + + /** + * Makes namespace from property name. + * + * @param {String} propertyName + * @returns {String|undefined} + */ + function makeNamespace(propertyName) { + var info = getPrefixInfo(propertyName); + return info && info.baseName; + } + + /** + * Creates object which contains info about vendor prefix used + * in propertyName. + * + * @param {String} propertyName property name + * @param {String} [namespace=''] namespace name + * @param {Number} [extraSymbols=0] extra symbols count + * @returns {Object|undefined} + */ + function getPrefixInfo(propertyName, namespace, extraSymbols) { + var baseName = propertyName; + var prefixLength = 0; + + namespace = namespace || ''; + extraSymbols = extraSymbols || 0; + + if (!propertyName) return; + + PREFIXES.some(function(prefix) { + prefix = '-' + prefix + '-'; + if (propertyName.indexOf(prefix) !== 0) return; + + baseName = baseName.substr(prefix.length); + prefixLength = prefix.length; + + return true; + }); + + return { + id: namespace + baseName, + baseName: baseName, + prefixLength: prefixLength, + extra: extraSymbols + }; + } + + /** + * Returns extra indent for item in arguments + * + * @param {Array} nodes nodes to process + * @returns {Number|undefined} + */ + function extraIndent(nodes) { + if (!nodes || !nodes.length) return; + + var node; + var crPos; + var tabPos; + var result = 0; + + for (var i = nodes.length; i--;) { + node = nodes[i]; + + if (!node.content) { + crPos = -1; + } else { + crPos = node.content.lastIndexOf('\n'); + tabPos = node.content.lastIndexOf('\t'); + if (tabPos > crPos) crPos = tabPos; + } + + if (crPos !== -1) + oneline = false; + + if (node.is('space')) { + result += node.content.length - crPos - 1; + if (crPos !== -1) + break; + } + if (node.is('multilineComment')) { + if (crPos === -1) { + // Comment symbols length + let offset = 4; + result += node.content.length + offset; + } else { + // Only last comment symbols length - 1 (not count \n) + let offset = crPos - 1; + result += node.content.length - offset; + break; + } + } + } + + return result; + } + + /** + * Wrapper for extra indent function for `property` node. + * + * @param {Array} nodes all nodes + * @param {Number} i position in nodes array + */ + function extraIndentProperty(nodes, i) { + var subset = []; + while (i--) { + if (!nodes.get(i) || nodes.get(i).is('declarationDelimiter')) + break; + subset.unshift(nodes.get(i)); + } + return extraIndent(subset); + } + + /** + * Wrapper for extra indent function for val-node. + * + * @param {Array} nodes all nodes + * @param {Number} i position in nodes array + */ + function extraIndentVal(nodes, i) { + var subset = []; + var declaration = nodes.get(i); + if (!declaration.is('declaration')) return; + + for (var x = declaration.length; x--;) { + if (!declaration.get(x).is('value')) continue; + + x--; + + while (!declaration.get(x).is('propertyDelimiter')) { + subset.push(declaration.get(x)); + x--; + } + + break; + } + return extraIndent(subset); + } + + /** + * Walks across nodes, and call payload for every node that pass + * selector check. + * + * @param {Object} args arguments in form of: + * { + * node: {object} current node, + * selector: {function} propertyName selector + * payload: {function} work to do with gathered info + * namespaceSelector: {function} selector for namespace + * getExtraSymbols: {Number} extra symbols count + * } + */ + function walk(args) { + args.node.forEach(function(item, i) { + var name = args.selector(item); + var namespace = args.namespaceSelector && + makeNamespace(args.namespaceSelector(item)); + var extraSymbols = args.getExtraSymbols(args.node, i); + + var info = name && getPrefixInfo(name, namespace, extraSymbols); + if (!info) return; + args.payload(info, i); + }); + } + + /** + * Returns property name. + * e.g. + * for: 'color: #fff' + * returns string: 'color' + * + * @param {node} node + * @returns {String|undefined} + */ + function getPropertyName(node) { + if (!node.is('declaration')) return; + // TODO: Check that it's not a variable + return node.get(0).get(0).content; + } + + /** + * Returns property value name. + * e.g. + * for: '-webkit-transition: -webkit-transform 150ms linear' + * returns string: '-webkit-transform', and + * for: 'background: -webkit-linear-gradient(...)' + * returns string: '-webkit-linear-gradient' + * + * @param {node} node + * @returns {String|undefined} + */ + function getValName(node) { + if (!node.is('declaration')) return; + + var value = node.first('value'); + if (value.get(0).is('ident')) return value.get(0).content; + if (value.get(0).is('function')) return value.get(0).get(0).content; + } + + /** + * Updates dict which contains info about items align. + * + * @param {Object} info + * @param {Object} dict + */ + function updateDict(info, dict) { + if (info.prefixLength === 0 && info.extra === 0) return; + + var indent = dict[info.id] || {prefixLength: 0, extra: 0}; + + let indentLength = indent.prefixLength + indent.extra; + let infoLength = info.prefixLength + info.extra; + if (indentLength > infoLength) { + dict[info.id] = indent; + } else { + dict[info.id] = { + prefixLength: info.prefixLength, + extra: info.extra + }; + } + } + + /** + * Returns string with correct number of spaces for info.baseName property. + * + * @param {Object} info + * @param {Object} dict + * @param {String} whitespaceNode + * @returns {String} + */ + function updateIndent(info, dict, whitespaceNode) { + var item = dict[info.id]; + if (!item) + return whitespaceNode; + + var crPos = whitespaceNode.lastIndexOf('\n'); + var tabPos = whitespaceNode.lastIndexOf('\t'); + if (tabPos > crPos) crPos = tabPos; + + var firstPart = whitespaceNode.substr(0, crPos + 1); + var extraIndent = new Array( + (item.prefixLength - info.prefixLength) + + (item.extra - info.extra) + + whitespaceNode.length - firstPart.length + + 1).join(' '); + + return firstPart.concat(extraIndent); + } + + return { + name: 'vendor-prefix-align', + + syntax: ['css', 'less', 'sass', 'scss'], + + accepts: { + boolean: [true] + }, + + /** + * Processes tree node. + * + * @param {node} ast + */ + process: function(ast) { + ast.traverseByType('block', function(node) { + oneline = true; + + var dict = {}; + + // Gathering Info + walk({ + node: node, + selector: getPropertyName, + getExtraSymbols: extraIndentProperty, + payload: function(info) { + updateDict(info, dict); + } + }); + + walk({ + node: node, + selector: getValName, + namespaceSelector: getPropertyName, + getExtraSymbols: extraIndentVal, + payload: function(info) { + updateDict(info, dict); + } + }); + + if (oneline && ast.syntax !== 'sass') return; + + // Update nodes + walk({ + node: node, + selector: getValName, + namespaceSelector: getPropertyName, + getExtraSymbols: extraIndentVal, + payload: function(info, i) { + for (var x = node.get(i).length; x--;) { + if (node.get(i).get(x).is('value')) break; + } + + let prevNode = node.get(i).get(x - 1); + if (!prevNode.is('space')) { + var space = gonzales.createNode({ + type: 'space', + content: '' + }); + node.get(i).insert(x, space); + ++x; + } + + let content = node.get(i).get(x - 1).content; + let updatedIndent = updateIndent(info, dict, content); + node.get(i).get(x - 1).content = updatedIndent; + } + }); + + if (ast.syntax === 'sass') return; + + walk({ + node: node, + selector: getPropertyName, + getExtraSymbols: extraIndentProperty, + payload: function(info, i) { + // `node.get(i - 1)` can be either space or comment: + var whitespaceNode = node.get(i - 1); + if (!whitespaceNode) return; + // If it's a comment, insert an empty space node: + if (!whitespaceNode.is('space')) { + whitespaceNode = gonzales.createNode({ + type: 'space', + content: '' + }); + node.insert(i - 1, whitespaceNode); + } + let content = whitespaceNode.content; + let updatedContent = updateIndent(info, dict, content); + whitespaceNode.content = updatedContent; + } + }); + }); + }, + + /** + * Detects the value of an option at the tree node. + * + * @param {node} ast + */ + detect: function(ast) { + let detected = []; + + ast.traverseByType('block', function(node) { + var result = { + true: 0, + false: 0 + }; + + var maybePrefix = false; + var prevPrefixLength = false; + var prevProp; + var prevSum; + var partialResult = null; + + var getResult = function(options) { + let {node, sum, info, i} = options; + var prop = info.baseName; + + // If this is the last item in a row and we have a result, + // then catch it + if (prop !== prevProp && partialResult !== null) { + if (partialResult) { + result.true++; + } else { + result.false++; + } + partialResult = null; + } + + if (prop === prevProp && + info.prefixLength !== prevPrefixLength) { + maybePrefix = true; + } else { + maybePrefix = false; + } + + if (maybePrefix && partialResult !== false) { + // If there is prefixed prop, check if the prefixes are + // aligned, but only if we hadn't already catched + // that it is false + if (sum === prevSum) { + partialResult = true; + } else { + partialResult = false; + } + } + + if (node.length === i + 3 && partialResult !== null) { + // If we're at the last property and have a result, + // catch it + if (partialResult) { + result.true++; + } else { + result.false++; + } + } + + prevPrefixLength = info.prefixLength; + prevProp = prop; + prevSum = sum; + }; + + // Gathering Info + walk({ + node: node, + selector: getPropertyName, + getExtraSymbols: extraIndentProperty, + payload: function(info, i) { + if (node.get(i - 1) && node.get(i - 1).content) { + let nodeLength = node.get(i - 1).content + .replace(/^[ \t]*\n+/, '').length; + var sum = nodeLength + info.prefixLength; + getResult({node: node, sum: sum, info: info, i: i}); + } + } + }); + + walk({ + node: node, + selector: getValName, + getExtraSymbols: extraIndentVal, + payload: function(info, i) { + for (var x = node.get(i).length; x--;) { + if (node.get(i).get(x).is('value')) break; + } + + if (node.get(i).get(x - 1)) { + let nodeLength = node.get(i).get(x - 1).content + .replace(/^[ \t]*\n+/, '').length; + var sum = nodeLength + info.prefixLength; + getResult({node: node, sum: sum, info: info, i: i}); + } + } + }); + + if (result.true > 0 || result.false > 0) { + if (result.true >= result.false) { + detected.push(true); + } else { + detected.push(false); + } + } + }); + + return detected; + } + }; +})(); diff --git a/src/plugin.js b/src/plugin.js new file mode 100644 index 00000000..a08046f8 --- /dev/null +++ b/src/plugin.js @@ -0,0 +1,96 @@ +'use strict'; + + +let Errors = require('./errors'); + +let Plugin = function(methods) { + for (let method in methods) { + this[method] = typeof method === 'function'? + methods[method].bind(this) : methods[method]; + } + + this.validate(); +}; + +Plugin.prototype = { + /** + * Plugin's name. + * @type {String} + */ + name: null, + + /** + * List of supported syntaxes. + * @type {Array} + */ + syntax: null, + + /** + * @type {Object} + */ + accepts: null, + + /** + * @type {Function} + */ + process: null, + + /** + * @type {Function} + */ + lint: null, + + value_: null, + get value() { + return this.value_; + }, + set value(value) { + let valueType = typeof value; + let pattern = this.accepts && this.accepts[valueType]; + + if (this.setValue) { + this.value_ = this.setValue(value); + return this.value_; + } + + if (!pattern) + throw new Error(Errors.unacceptableValueType(valueType, this.accepts)); + + if (valueType === 'boolean') { + if (pattern.indexOf(value) < 0) + throw new Error(Errors.unacceptableBoolean(pattern)); + this.value_ = value; + return this.value_; + } + + if (valueType === 'number') { + if (value !== parseInt(value)) + throw new Error(Errors.unacceptableNumber()); + this.value_ = new Array(value + 1).join(' '); + return this.value_; + } + + if (valueType = 'string') { + if (!value.match(pattern)) + throw new Error(Errors.unacceptableString(pattern)); + this.value_ = value; + return this.value_; + } + + throw new Error(Errors.implementSetValue(valueType)); + }, + + validate() { + if (typeof this.name !== 'string' || !this.name) + throw new Error(Errors.missingName()); + + if (!Array.isArray(this.syntax) || this.syntax.length === 0) + throw new Error(Errors.missingSyntax()); + + if (typeof this.accepts !== 'object' && + typeof this.setValue !== 'function') + throw new Error(Errors.missingSetValue()); + } +}; + +module.exports = Plugin; diff --git a/test/core/configure.js b/test/core/configure.js deleted file mode 100644 index 77d9fe51..00000000 --- a/test/core/configure.js +++ /dev/null @@ -1,48 +0,0 @@ -var Comb = process.env.TEST_COV ? require('../../lib-cov/csscomb') : require('../../lib/csscomb'); -var assert = require('assert'); - -describe('csscomb methods', function() { - var comb; - var input; - var output; - var expected; - - it('Passing no config to constructor should not configure anything', function() { - comb = new Comb(); - assert.equal(undefined, comb._handlers); - }); - - it('Passing valid config name to constructor should configure using correct config', function() { - comb = new Comb('zen'); - input = 'a { color: tomato; top: 0; }'; - expected = 'a {top: 0; color: tomato; }'; - output = comb.processString(input); - - assert.equal(expected, output); - }); - - it('Passing config object to constructor should configure using that object', function() { - comb = new Comb({ 'always-semicolon': true }); - input = 'a { color: tomato }'; - expected = 'a { color: tomato; }'; - output = comb.processString(input); - - assert.equal(expected, output); - }); - - it('new Comb() should be chainable', function() { - input = 'a { color: tomato; top: 0; }'; - expected = 'a {top: 0; color: tomato; }'; - output = new Comb('zen').processString(input); - - assert.equal(expected, output); - }); - - it('configure() should be chainable', function() { - input = 'a { color: tomato }'; - expected = 'a { color: tomato; }'; - output = new Comb().configure({ 'always-semicolon': true }).processString(input); - - assert.equal(expected, output); - }); -}); diff --git a/test/core/configure/test.js b/test/core/configure/test.js new file mode 100644 index 00000000..4743df40 --- /dev/null +++ b/test/core/configure/test.js @@ -0,0 +1,47 @@ +let Comb = process.env.TEST_COV ? + require('../../../lib-cov/csscomb') : + require('../../../lib/csscomb'); +let assert = require('assert'); + +describe('CSScomb#configure', function() { + it('Passing no config to constructor should not configure anything', function() { + let comb = new Comb(); + assert.equal(undefined, comb._handlers); + }); + + it('Passing valid config name to constructor should configure using correct config', function() { + let comb = new Comb('zen'); + let input = 'a { color: tomato; top: 0; }'; + let expected = 'a {top: 0; color: tomato; }'; + return comb.processString(input).then(function(output) { + assert.equal(expected, output); + }); + }); + + it('Passing config object to constructor should configure using that object', function() { + let comb = new Comb({ 'always-semicolon': true }); + let input = 'a { color: tomato }'; + let expected = 'a { color: tomato; }'; + return comb.processString(input).then(function(actual) { + assert.equal(actual, expected); + }); + }); + + it('new Comb() should be chainable', function() { + let input = 'a { color: tomato; top: 0; }'; + let expected = 'a {top: 0; color: tomato; }'; + return (new Comb('zen')).processString(input).then(output => { + assert.equal(expected, output); + }); + }); + + it('configure() should be chainable', function() { + let input = 'a { color: tomato }'; + let expected = 'a { color: tomato; }'; + return (new Comb()).configure({ 'always-semicolon': true }) + .processString(input) + .then(actual => { + assert.equal(actual, expected); + }); + }); +}); diff --git a/test/core/core_test.js b/test/core/core_test.js new file mode 100644 index 00000000..4384753d --- /dev/null +++ b/test/core/core_test.js @@ -0,0 +1,54 @@ +let assert = require('assert'); +let fs = require('fs'); +let path = require('path'); + +let Comb = require('../../lib/csscomb'); + +class CoreTest { + constructor(context, config) { + this.file = context.test.file; + this.syntax = context.test.parent.title; + + this.Comb = Comb; + this.comb = new Comb(); + if (config) this.comb.configure(config); + } + + useConfig(name) { + let config = Comb.getConfig(name); + this.comb.configure(config); + } + + getErrors(filename) { + let input = this.readFile(filename); + return this.comb.lintString(input, {syntax: this.syntax}); + } + + shouldBeEqual(inputFile, expectedFile) { + let input = this.readFile(inputFile); + let expected = expectedFile ? this.readFile(expectedFile) : input; + + return this.comb.processString(input, {syntax: this.syntax}) + .then(string => assert.equal(string, expected)); + } + + /** + * Detect options in a file and compare result with expected. + * File names should be relative to test suite's folder. + * @param {Array} options List of options that should be detected + * @param {String} input Name of template file + * @param {Object} expected Expected config with detected options + */ + shouldDetect(options, input, expected) { + let detectedConfig = Comb.detectInString(input, options); + assert.deepEqual(detectedConfig, expected); + } + + readFile(filename) { + let dirname = path.dirname(this.file); + let filePath = path.join(dirname, filename); + return fs.readFileSync(filePath, 'utf8'); + } +} + +module.exports = CoreTest; diff --git a/test/core/get-config.js b/test/core/get-config.js deleted file mode 100644 index b3f23a3a..00000000 --- a/test/core/get-config.js +++ /dev/null @@ -1,51 +0,0 @@ -var assert = require('assert'); - -describe('csscomb methods', function() { - it('getConfig()', function() { - var config = require('../../config/csscomb.json'); - - assert.equal(this.comb.getConfig(), config); - }); - - it('getConfig(number)', function() { - assert.throws(function() { - this.comb.getConfig(16); - }); - }); - - it('getConfig(boolean)', function() { - assert.throws(function() { - this.comb.getConfig(true); - }); - }); - - it('getConfig(empty string)', function() { - var config = require('../../config/csscomb.json'); - - assert.equal(this.comb.getConfig(''), config); - }); - - it('getConfig(invalid string)', function() { - assert.throws(function() { - this.comb.getConfig('nani'); - }); - }); - - it('getConfig(csscomb)', function() { - var config = require('../../config/csscomb.json'); - - assert.equal(this.comb.getConfig('csscomb'), config); - }); - - it('getConfig(zen)', function() { - var config = require('../../config/zen.json'); - - assert.equal(this.comb.getConfig('zen'), config); - }); - - it('getConfig(yandex)', function() { - var config = require('../../config/yandex.json'); - - assert.equal(this.comb.getConfig('yandex'), config); - }); -}); diff --git a/test/core/get-config/test.js b/test/core/get-config/test.js new file mode 100644 index 00000000..8db70a2f --- /dev/null +++ b/test/core/get-config/test.js @@ -0,0 +1,63 @@ +var assert = require('assert'); +let Test = require('../core_test'); + +describe('csscomb methods', function() { + it('getConfig()', function() { + let test = new Test(this); + var config = require('../../../config/csscomb.json'); + + assert.equal(test.Comb.getConfig(), config); + }); + + it('getConfig(number)', function() { + let test = new Test(this); + + assert.throws(function() { + test.Comb.getConfig(16); + }); + }); + + it('getConfig(boolean)', function() { + let test = new Test(this); + + assert.throws(function() { + test.Comb.getConfig(true); + }); + }); + + it('getConfig(empty string)', function() { + let test = new Test(this); + var config = require('../../../config/csscomb.json'); + + assert.equal(test.Comb.getConfig(''), config); + }); + + it('getConfig(invalid string)', function() { + let test = new Test(this); + + assert.throws(function() { + test.Comb.getConfig('nani'); + }); + }); + + it('getConfig(csscomb)', function() { + let test = new Test(this); + var config = require('../../../config/csscomb.json'); + + assert.equal(test.Comb.getConfig('csscomb'), config); + }); + + it('getConfig(zen)', function() { + let test = new Test(this); + var config = require('../../../config/zen.json'); + + assert.equal(test.Comb.getConfig('zen'), config); + }); + + it('getConfig(yandex)', function() { + let test = new Test(this); + var config = require('../../../config/yandex.json'); + + assert.equal(test.Comb.getConfig('yandex'), config); + }); +}); diff --git a/test/core/less.js b/test/core/less.js deleted file mode 100644 index dc5fefe1..00000000 --- a/test/core/less.js +++ /dev/null @@ -1,42 +0,0 @@ -describe('LESS', function() { - beforeEach(function() { - this.filename = __filename; - this.comb.configure({}); - }); - - it('Should parse nested rules', function() { - this.shouldBeEqual('nested-rule.less'); - }); - - it('Should parse operations', function() { - this.shouldBeEqual('operation.less'); - }); - - it('Should parse parent selector &', function() { - this.shouldBeEqual('parent-selector.less'); - }); - - it('Should parse variables', function() { - this.shouldBeEqual('variable.less'); - }); - - it('Should parse interpolated variables inside selectors', function() { - this.shouldBeEqual('interpolated-variable-1.less'); - }); - - it('Should parse interpolated variables inside values', function() { - this.shouldBeEqual('interpolated-variable-2.less'); - }); - - it('Should parse @import', function() { - this.shouldBeEqual('import.less'); - }); - - it('Should parse included mixins', function() { - this.shouldBeEqual('mixin.less'); - }); - - it('Should parse nested @media', function() { - this.shouldBeEqual('nested-media.less'); - }); -}); diff --git a/test/core/less/test.js b/test/core/less/test.js new file mode 100644 index 00000000..72a54eb4 --- /dev/null +++ b/test/core/less/test.js @@ -0,0 +1,66 @@ +let Test = require('../core_test'); + +describe('less', function() { + it('Should parse nested rules', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('nested-rule.less'); + }); + + it('Should parse operations', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('operation.less'); + }); + + it('Should parse parent selector &', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('parent-selector.less'); + }); + + it('Should parse variables', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('variable.less'); + }); + + it('Should parse interpolated variables inside selectors', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('interpolated-variable-1.less'); + }); + + it('Should parse interpolated variables inside values', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('interpolated-variable-2.less'); + }); + + it('Should parse @import', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('import.less'); + }); + + it('Should parse included mixins', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('mixin.less'); + }); + + it('Should parse nested @media', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('nested-media.less'); + }); +}); diff --git a/test/core/scss.js b/test/core/scss.js deleted file mode 100644 index 925a116a..00000000 --- a/test/core/scss.js +++ /dev/null @@ -1,107 +0,0 @@ -describe('SCSS', function() { - beforeEach(function() { - this.filename = __filename; - this.comb.configure({}); - }); - - it('Should parse nested rules', function() { - this.shouldBeEqual('nested-rule.scss'); - }); - - it('Should parse parent selector &', function() { - this.shouldBeEqual('parent-selector.scss'); - }); - - it('Should parse nested properties', function() { - this.shouldBeEqual('nested-property.scss'); - }); - - it('Should parse variables', function() { - this.shouldBeEqual('variable.scss'); - }); - - it('Should parse interpolated variables inside selectors', function() { - this.shouldBeEqual('interpolated-variable-1.scss'); - }); - - it('Should parse interpolated variables inside values', function() { - this.shouldBeEqual('interpolated-variable-2.scss'); - }); - - it('Should parse defaults', function() { - this.shouldBeEqual('default.scss'); - }); - - it('Should parse @import', function() { - this.shouldBeEqual('import.scss'); - }); - - it('Should parse @include', function() { - this.shouldBeEqual('include.scss'); - }); - - it('Should parse nested @media', function() { - this.shouldBeEqual('nested-media.scss'); - }); - - it('Should parse @extend with classes', function() { - this.shouldBeEqual('extend-1.scss'); - }); - - it('Should parse @extend with placeholders', function() { - this.shouldBeEqual('extend-2.scss'); - }); - - it('Should parse @warn', function() { - this.shouldBeEqual('warn.scss'); - }); - - it('Should parse @if', function() { - this.shouldBeEqual('if.scss'); - }); - - it('Should parse @if and @else', function() { - this.shouldBeEqual('if-else.scss'); - }); - - it('Should parse @if and @else if', function() { - this.shouldBeEqual('if-else-if.scss'); - }); - - it('Should parse @for', function() { - this.shouldBeEqual('for.scss'); - }); - - it('Should parse @each', function() { - this.shouldBeEqual('each.scss'); - }); - - it('Should parse @while', function() { - this.shouldBeEqual('while.scss'); - }); - - it('Should parse mixins', function() { - this.shouldBeEqual('mixin-1.scss'); - }); - - it('Should parse passing several variables to a mixin', function() { - this.shouldBeEqual('mixin-2.scss'); - }); - - it('Should parse passing a list of variables to a mixin', function() { - this.shouldBeEqual('mixin-3.scss'); - }); - - it('Should parse passing a content block to a mixin', function() { - this.shouldBeEqual('mixin-4.scss'); - }); - - it('Should parse @content', function() { - this.shouldBeEqual('content.scss'); - }); - - it('Should parse functions', function() { - this.shouldBeEqual('function.scss'); - }); -}); - diff --git a/test/core/scss/empty.scss b/test/core/scss/empty.scss new file mode 100644 index 00000000..e69de29b diff --git a/test/core/scss/test.js b/test/core/scss/test.js new file mode 100644 index 00000000..06c50d67 --- /dev/null +++ b/test/core/scss/test.js @@ -0,0 +1,186 @@ +let Test = require('../core_test'); + +describe('scss', function() { + it('Should parse nested rules', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('nested-rule.scss'); + }); + + it('Should parse parent selector &', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('parent-selector.scss'); + }); + + it('Should parse nested properties', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('nested-property.scss'); + }); + + it('Should parse variables', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('variable.scss'); + }); + + it('Should parse interpolated variables inside selectors', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('interpolated-variable-1.scss'); + }); + + it('Should parse interpolated variables inside values', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('interpolated-variable-2.scss'); + }); + + it('Should parse defaults', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('default.scss'); + }); + + it('Should parse @import', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('import.scss'); + }); + + it('Should parse @include', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('include.scss'); + }); + + it('Should parse nested @media', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('nested-media.scss'); + }); + + it('Should parse @extend with classes', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('extend-1.scss'); + }); + + it('Should parse @extend with placeholders', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('extend-2.scss'); + }); + + it('Should parse @warn', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('warn.scss'); + }); + + it('Should parse @if', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('if.scss'); + }); + + it('Should parse @if and @else', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('if-else.scss'); + }); + + it('Should parse @if and @else if', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('if-else-if.scss'); + }); + + it('Should parse @for', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('for.scss'); + }); + + it('Should parse @each', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('each.scss'); + }); + + it('Should parse @while', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('while.scss'); + }); + + it('Should parse mixins', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('mixin-1.scss'); + }); + + it('Should parse passing several variables to a mixin', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('mixin-2.scss'); + }); + + it('Should parse passing a list of variables to a mixin', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('mixin-3.scss'); + }); + + it('Should parse passing a content block to a mixin', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('mixin-4.scss'); + }); + + it('Should parse @content', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('content.scss'); + }); + + it('Should parse an empty file', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('empty.scss'); + }); + + it('Should parse functions', function() { + let test = new Test(this); + test.comb.configure({}); + + return test.shouldBeEqual('function.scss'); + }); +}); + diff --git a/test/core/should-process.js b/test/core/should-process.js deleted file mode 100644 index f0223e6d..00000000 --- a/test/core/should-process.js +++ /dev/null @@ -1,40 +0,0 @@ -var assert = require('assert'); - -describe('csscomb methods', function() { - beforeEach(function() { - this.comb.configure({ exclude: ['nani/*', 'foo', 'b.css'] }); - }); - - it('shouldProcess(path)', function() { - assert.equal(true, this.comb._shouldProcess('styles')); - }); - - it('shouldProcess(excluded path)', function() { - assert.equal(false, this.comb._shouldProcess('foo')); - }); - - it('shouldProcessFile(css)', function() { - assert.equal(true, this.comb._shouldProcessFile('a.css')); - }); - - it('shouldProcessFile(scss)', function() { - assert.equal(true, this.comb._shouldProcessFile('a.scss')); - }); - - it('shouldProcessFile(less)', function() { - assert.equal(true, this.comb._shouldProcessFile('a.less')); - }); - - it('shouldProcessFile(txt)', function() { - assert.equal(false, this.comb._shouldProcessFile('a.txt')); - }); - - it('shouldProcessFile(css, excluded directory)', function() { - assert.equal(false, this.comb._shouldProcessFile('nani/a.css')); - }); - - it('shouldProcessFile(css, excluded file)', function() { - assert.equal(false, this.comb._shouldProcessFile('b.css')); - }); -}); - diff --git a/test/core/use/test.js b/test/core/use/test.js new file mode 100644 index 00000000..1d3a9aa7 --- /dev/null +++ b/test/core/use/test.js @@ -0,0 +1,42 @@ +var assert = require('assert'); +let Test = require('../core_test'); + +describe('.use()', function() { + it('Should set predefined options in correct order', function() { + let test = new Test(this); + var config = test.Comb.getConfig('csscomb'); + test.comb.configure(config); + var options = test.comb.plugins.map(function(plugin) { + return plugin.name; + }); + var expected = [ + 'always-semicolon', + 'lines-between-rulesets', + 'remove-empty-rulesets', + 'color-case', + 'color-shorthand', + 'element-case', + 'eof-newline', + 'leading-zero', + 'quotes', + 'sort-order-fallback', + 'space-after-colon', + 'space-after-combinator', + 'space-after-opening-brace', + 'space-after-selector-delimiter', + 'space-before-colon', + 'space-before-combinator', + 'space-before-opening-brace', + 'space-before-selector-delimiter', + 'space-between-declarations', + 'block-indent', + 'sort-order', + 'strip-spaces', + 'space-before-closing-brace', + 'unitless-zero', + 'tab-size', + 'vendor-prefix-align' + ]; + assert.deepEqual(options, expected); + }); +}); diff --git a/test/mocha.js b/test/mocha.js index e9d4cdd9..98e0ee7b 100644 --- a/test/mocha.js +++ b/test/mocha.js @@ -1,92 +1,17 @@ -var Comb = process.env.TEST_COV ? - require('../lib-cov/csscomb') : require('../lib/csscomb'); -var Mocha = require('mocha'); -var assert = require('assert'); -var fs = require('fs'); -var path = require('path'); +let glob = require('glob'); +let Mocha = require('mocha'); -var mocha = new Mocha(); +let mocha = new Mocha(); if (process.env.TEST_COV) mocha.reporter('html-cov'); // Tell mocha which tests to run: -['test/core', 'test/options'].forEach(function(dirname) { - fs.readdirSync(dirname).forEach(function(file) { - mocha.addFile(path.join(dirname, file)); - }); +glob.sync('test/**/test.js').forEach(file => { + mocha.addFile(file); }); -// Add helpers (see tests for usage examples): -mocha.suite.beforeEach(function() { - this.comb = new Comb(); - this.filename = ''; - - /** - * Read css file from test suite's directory. - * If run inside `test/core/cli.js` file, `this.readFile('nani.css')` will - * return content of `test/core/cli/nani.css` file. - * `this.filename = __filename` is required if this helper is used in a test - * case (see tests for examples). - * @param {String} filename Name of file that is located in test folder - * @returns {String} File's content - */ - this.readFile = function(filename) { - // Remove `.js` from test suite's name: - var dirname = this.filename.slice(0, -3); - return fs.readFileSync(dirname + '/' + filename, 'utf8'); - }; - - /** - * Comb a file and compare the result with expected. - * If `expected` is not defined, check that file's content does not change - * after combing. - * File names should be relative to test suite's folder. - * @param {String} input Name of file that should be combed - * @param {String} [expected] Name of file with expected content - */ - this.shouldBeEqual = function(input, expected) { - var syntax = input.split('.').pop(); - input = this.readFile(input); - expected = expected ? this.readFile(expected) : input; - assert.equal(this.comb.processString(input, syntax), expected); - }; - - /** - * Detect options in a file and compare result with expected. - * File names should be relative to test suite's folder. - * @param {Array} options List of options that should be detected - * @param {String} input Name of template file - * @param {Object} expected Expected config with detected options - */ - this.shouldDetect = function(options, input, expected) { - // We need to “sort” the input and expected objects, as their order may vary - function sortObject(o) { - var sorted = {}; - var key = []; - var a = []; - - for (key in o) { - if (o.hasOwnProperty(key)) { - a.push(key); - } - } - - a.sort(); - - for (key = 0; key < a.length; key++) { - sorted[a[key]] = o[a[key]]; - } - return sorted; - } - assert.equal( - JSON.stringify(sortObject(this.comb.detectInString(input, options))), - JSON.stringify(sortObject(expected)) - ); - }; -}); - -mocha.run(function(failures) { - process.on('exit', function() { - process.exit(failures); - }); +mocha.run(failures => { + process.on('exit', () => { + process.exit(failures); + }); }); diff --git a/test/options/always-semicolon-less.js b/test/options/always-semicolon-less.js deleted file mode 100644 index b366c12c..00000000 --- a/test/options/always-semicolon-less.js +++ /dev/null @@ -1,54 +0,0 @@ -describe('options/always-semicolon (scss)', function() { - beforeEach(function() { - this.filename = __filename; - this.comb.configure({ 'always-semicolon': true }); - }); - - it('Should not add semicolon to condition (single-line style)', function() { - this.shouldBeEqual('condition.less'); - }); - - it('Should not add semicolon to condition (multi-line style)', function() { - this.shouldBeEqual('condition-multiline.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 1 (single-line style)', function() { - this.shouldBeEqual('include-1.less', 'include-1.expected.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 1 (multi-line style)', function() { - this.shouldBeEqual('include-1-multiline.less', 'include-1-multiline.expected.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 2 (single-line style)', function() { - this.shouldBeEqual('include-2.less', 'include-2.expected.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 2 (multi-line style)', function() { - this.shouldBeEqual('include-2-multiline.less', 'include-2-multiline.expected.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 3 (single-line style)', function() { - this.shouldBeEqual('include-3.less', 'include-3.expected.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 3 (multi-line style)', function() { - this.shouldBeEqual('include-3-multiline.less', 'include-3-multiline.expected.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 4 (single-line style)', function() { - this.shouldBeEqual('include-4.less', 'include-4.expected.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 4 (multi-line style)', function() { - this.shouldBeEqual('include-4-multiline.less', 'include-4-multiline.expected.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 5 (single-line style)', function() { - this.shouldBeEqual('include-5.less', 'include-5.expected.less'); - }); - - it('Should add semicolon to last included mixin if missing. Test 5 (multi-line style)', function() { - this.shouldBeEqual('include-5-multiline.less', 'include-5-multiline.expected.less'); - }); -}); diff --git a/test/options/always-semicolon-less/condition.less b/test/options/always-semicolon-less/condition.less deleted file mode 100644 index c0c4950f..00000000 --- a/test/options/always-semicolon-less/condition.less +++ /dev/null @@ -1 +0,0 @@ -div { @color: tomato; when (@color = tomato) { top: 0; } } diff --git a/test/options/always-semicolon-scss.js b/test/options/always-semicolon-scss.js deleted file mode 100644 index 48ec6ec5..00000000 --- a/test/options/always-semicolon-scss.js +++ /dev/null @@ -1,62 +0,0 @@ -describe('options/always-semicolon (scss)', function() { - beforeEach(function() { - this.filename = __filename; - this.comb.configure({ 'always-semicolon': true }); - }); - - it('Should not add semicolon if last value is block (singl-line style)', function() { - this.shouldBeEqual('block-value.scss'); - }); - - it('Should not add semicolon if last value is block (multi-line style)', function() { - this.shouldBeEqual('block-value-multiline.scss'); - }); - - it('Should add semicolon to last included mixin if missing. Test 1 (single-line style)', function() { - this.shouldBeEqual('include-1.scss', 'include-1.expected.scss'); - }); - - it('Should add semicolon to last included mixin if missing. Test 1 (multi-line style)', function() { - this.shouldBeEqual('include-1-multiline.scss', 'include-1-multiline.expected.scss'); - }); - - it('Should add semicolon to last included mixin if missing. Test 2 (single-line style)', function() { - this.shouldBeEqual('include-2.scss', 'include-2.expected.scss'); - }); - - it('Should add semicolon to last included mixin if missing. Test 2 (multi-line style)', function() { - this.shouldBeEqual('include-2-multiline.scss', 'include-2-multiline.expected.scss'); - }); - - it('Should not add semicolon to last included mixin if there is a block (single-line style)', function() { - this.shouldBeEqual('block-include.scss'); - }); - - it('Should not add semicolon to last included mixin if there is a block (multi-line style)', function() { - this.shouldBeEqual('block-include-multiline.scss'); - }); - - it('Should add semicolon to last extend if missing (single-line style)', function() { - this.shouldBeEqual('extend.scss', 'extend.expected.scss'); - }); - - it('Should add semicolon to last extend if missing (multi-line style)', function() { - this.shouldBeEqual('extend-multiline.scss', 'extend-multiline.expected.scss'); - }); - - it('Should not add semicolon to condition (single-line style)', function() { - this.shouldBeEqual('condition.scss'); - }); - - it('Should not add semicolon to condition (multi-line style)', function() { - this.shouldBeEqual('condition-multiline.scss'); - }); - - it('Should not add semicolon to loop (single-line style)', function() { - this.shouldBeEqual('loop.scss'); - }); - - it('Should not add semicolon to loop (multi-line style)', function() { - this.shouldBeEqual('loop-multiline.scss'); - }); -}); diff --git a/test/options/always-semicolon.js b/test/options/always-semicolon.js deleted file mode 100644 index a20f8d3f..00000000 --- a/test/options/always-semicolon.js +++ /dev/null @@ -1,125 +0,0 @@ -var assert = require('assert'); - -describe('options/always-semicolon', function() { - it('Should add semicolon for last property if missing. Test 1', function() { - this.comb.configure({ 'always-semicolon': true }); - assert.equal( - this.comb.processString( - 'div { height: 0 }' - ), - 'div { height: 0; }' - ); - }); - - it('Should add semicolon for last property if missing. Test 2', function() { - this.comb.configure({ 'always-semicolon': true }); - assert.equal( - this.comb.processString( - 'div {\nheight: 0\n}' - ), - 'div {\nheight: 0;\n}' - ); - }); - - it('Should add semicolon for last property if missing. Test 3', function() { - this.comb.configure({ 'always-semicolon': true }); - assert.equal( - this.comb.processString( - 'div {height: 0}' - ), - 'div {height: 0;}' - ); - }); - - it('Should add semicolon for last property if missing. Test 4', function() { - this.comb.configure({ 'always-semicolon': true }); - assert.equal( - this.comb.processString( - 'div {\nheight: 0 /* Comment */\n}' - ), - 'div {\nheight: 0; /* Comment */\n}' - ); - }); - - it('Should add semicolon for last property if missing. Test 5', function() { - this.comb.configure({ 'always-semicolon': true }); - assert.equal( - this.comb.processString( - 'div {\ntop: 1px;\nheight: 0 /* 1comment */ /* 2comment */\n}' - ), - 'div {\ntop: 1px;\nheight: 0; /* 1comment */ /* 2comment */\n}' - ); - }); - - it('Should detect semicolon for last property. Test 1', function() { - this.shouldDetect( - ['always-semicolon'], - 'div { height: 0 }', - { - 'always-semicolon': false - } - ); - }); - - it('Should detect semicolon for last property. Test 2', function() { - this.shouldDetect( - ['always-semicolon'], - 'div { height: 0; }', - { - 'always-semicolon': true - } - ); - }); - - it('Should detect semicolon for last property. Test 3', function() { - this.shouldDetect( - ['always-semicolon'], - 'div { height: 0; } div { height: 0 }', - { - 'always-semicolon': true - } - ); - }); - - it('Should detect semicolon for last property. Test 4', function() { - this.shouldDetect( - ['always-semicolon'], - 'div { height: 0 } div { height: 0; } div { height: 0 }', - { - 'always-semicolon': false - } - ); - }); - - it('Should detect semicolon for last property. Test 5', function() { - this.shouldDetect( - ['always-semicolon'], - 'div {\nheight: 0 /* Comment */\n} ' + - 'div { height: 0; }' + - 'div {\ntop: 1px;\nheight: 0 /* 1comment */ /* 2comment */\n}', - { - 'always-semicolon': false - } - ); - }); - - - it('Should detect semicolon for last property. Test 6', function() { - this.shouldDetect( - ['always-semicolon'], - 'a{\n border:0;\n}', - { - 'always-semicolon': true - } - ); - }); - - it('Should not detect semicolon for last property if there are no properties', function() { - this.shouldDetect( - ['always-semicolon'], - 'div {}', - {} - ); - }); - -}); diff --git a/test/options/always-semicolon/detect/css/test-1.css b/test/options/always-semicolon/detect/css/test-1.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-1.expected.css b/test/options/always-semicolon/detect/css/test-1.expected.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-2.css b/test/options/always-semicolon/detect/css/test-2.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-2.expected.css b/test/options/always-semicolon/detect/css/test-2.expected.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-3.css b/test/options/always-semicolon/detect/css/test-3.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-3.expected.css b/test/options/always-semicolon/detect/css/test-3.expected.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-4.css b/test/options/always-semicolon/detect/css/test-4.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-4.expected.css b/test/options/always-semicolon/detect/css/test-4.expected.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-5.css b/test/options/always-semicolon/detect/css/test-5.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-5.expected.css b/test/options/always-semicolon/detect/css/test-5.expected.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-6.css b/test/options/always-semicolon/detect/css/test-6.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-6.expected.css b/test/options/always-semicolon/detect/css/test-6.expected.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-7.css b/test/options/always-semicolon/detect/css/test-7.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/css/test-7.expected.css b/test/options/always-semicolon/detect/css/test-7.expected.css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/always-semicolon/detect/test.js b/test/options/always-semicolon/detect/test.js new file mode 100644 index 00000000..6d22001a --- /dev/null +++ b/test/options/always-semicolon/detect/test.js @@ -0,0 +1,72 @@ +let Test = require('../../option_test'); + +describe('Option `always-semicolon`, detect', function() { + describe('css', function() { + it('Should detect semicolon for last property. Test 1', function() { + let test = new Test(this); + test.shouldDetect( + ['always-semicolon'], + 'div { height: 0 }', + {'always-semicolon': false} + ); + }); + + it('Should detect semicolon for last property. Test 2', function() { + let test = new Test(this); + test.shouldDetect( + ['always-semicolon'], + 'div { height: 0; }', + {'always-semicolon': true} + ); + }); + + it('Should detect semicolon for last property. Test 3', function() { + let test = new Test(this); + test.shouldDetect( + ['always-semicolon'], + 'div { height: 0; } div { height: 0 }', + {'always-semicolon': true} + ); + }); + + it('Should detect semicolon for last property. Test 4', function() { + let test = new Test(this); + test.shouldDetect( + ['always-semicolon'], + 'div { height: 0 } div { height: 0; } div { height: 0 }', + {'always-semicolon': false} + ); + }); + + it('Should detect semicolon for last property. Test 5', function() { + let test = new Test(this); + test.shouldDetect( + ['always-semicolon'], + 'div {\nheight: 0 /* Comment */\n} ' + + 'div { height: 0; }' + + 'div {\ntop: 1px;\nheight: 0 /* 1comment */ /* 2comment */\n}', + {'always-semicolon': false} + ); + }); + + + it('Should detect semicolon for last property. Test 6', function() { + let test = new Test(this); + test.shouldDetect( + ['always-semicolon'], + 'a{\n border:0;\n}', + {'always-semicolon': true} + ); + }); + + it('Should not detect semicolon for last property if there are no properties', function() { + let test = new Test(this); + test.shouldDetect( + ['always-semicolon'], + 'div {}', + {} + ); + }); + }); +}); + diff --git a/test/options/always-semicolon/lint/css/lint-1.css b/test/options/always-semicolon/lint/css/lint-1.css new file mode 100644 index 00000000..0dbee0fe --- /dev/null +++ b/test/options/always-semicolon/lint/css/lint-1.css @@ -0,0 +1 @@ +div { height: 0; } diff --git a/test/options/always-semicolon/lint/css/lint-2.css b/test/options/always-semicolon/lint/css/lint-2.css new file mode 100644 index 00000000..f36f0139 --- /dev/null +++ b/test/options/always-semicolon/lint/css/lint-2.css @@ -0,0 +1 @@ +div { height: 0 } diff --git a/test/options/always-semicolon/lint/css/lint-3.css b/test/options/always-semicolon/lint/css/lint-3.css new file mode 100644 index 00000000..e47a2a9c --- /dev/null +++ b/test/options/always-semicolon/lint/css/lint-3.css @@ -0,0 +1,9 @@ +div { + height: 0 +} + +@media screen { + .a { + color: tomato; + position: panda} +} diff --git a/test/options/always-semicolon/lint/test.js b/test/options/always-semicolon/lint/test.js new file mode 100644 index 00000000..80cb27de --- /dev/null +++ b/test/options/always-semicolon/lint/test.js @@ -0,0 +1,44 @@ +let assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `always-semicolon`, lint', function() { + describe('css', function() { + it('Should report no errors', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.getErrors('lint-1.css').then(errors => { + assert.equal(errors.length, 0); + }); + }); + + it('Error mesage should be a string', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.getErrors('lint-2.css').then(errors => { + let error = errors[0]; + assert.equal(typeof error.message, 'string'); + }); + }); + + it('Error should provide correct position info', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.getErrors('lint-2.css').then(errors => { + let error = errors[0]; + assert.equal(error.line, 1); + assert.equal(error.column, 16); + }); + }); + + it('Should report multiple errors', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.getErrors('lint-3.css').then(errors => { + assert.equal(errors.length, 2); + + assert.equal(errors[0].line, 2); + assert.equal(errors[0].column, 14); + + assert.equal(errors[1].line, 8); + assert.equal(errors[1].column, 24); + }); + }); + }); +}); + diff --git a/test/options/always-semicolon/process/css/test-1.css b/test/options/always-semicolon/process/css/test-1.css new file mode 100644 index 00000000..f36f0139 --- /dev/null +++ b/test/options/always-semicolon/process/css/test-1.css @@ -0,0 +1 @@ +div { height: 0 } diff --git a/test/options/always-semicolon/process/css/test-1.expected.css b/test/options/always-semicolon/process/css/test-1.expected.css new file mode 100644 index 00000000..0dbee0fe --- /dev/null +++ b/test/options/always-semicolon/process/css/test-1.expected.css @@ -0,0 +1 @@ +div { height: 0; } diff --git a/test/options/always-semicolon/process/css/test-2.css b/test/options/always-semicolon/process/css/test-2.css new file mode 100644 index 00000000..c1888d9f --- /dev/null +++ b/test/options/always-semicolon/process/css/test-2.css @@ -0,0 +1,3 @@ +div { +height: 0 +} diff --git a/test/options/always-semicolon/process/css/test-2.expected.css b/test/options/always-semicolon/process/css/test-2.expected.css new file mode 100644 index 00000000..d5a75f33 --- /dev/null +++ b/test/options/always-semicolon/process/css/test-2.expected.css @@ -0,0 +1,3 @@ +div { +height: 0; +} diff --git a/test/options/always-semicolon/process/css/test-3.css b/test/options/always-semicolon/process/css/test-3.css new file mode 100644 index 00000000..8840e0e0 --- /dev/null +++ b/test/options/always-semicolon/process/css/test-3.css @@ -0,0 +1 @@ +div {height: 0} diff --git a/test/options/always-semicolon/process/css/test-3.expected.css b/test/options/always-semicolon/process/css/test-3.expected.css new file mode 100644 index 00000000..8fe52809 --- /dev/null +++ b/test/options/always-semicolon/process/css/test-3.expected.css @@ -0,0 +1 @@ +div {height: 0;} diff --git a/test/options/always-semicolon/process/css/test-4.css b/test/options/always-semicolon/process/css/test-4.css new file mode 100644 index 00000000..d72a31ce --- /dev/null +++ b/test/options/always-semicolon/process/css/test-4.css @@ -0,0 +1,3 @@ +div { +height: 0 /* Comment */ +} diff --git a/test/options/always-semicolon/process/css/test-4.expected.css b/test/options/always-semicolon/process/css/test-4.expected.css new file mode 100644 index 00000000..4710445d --- /dev/null +++ b/test/options/always-semicolon/process/css/test-4.expected.css @@ -0,0 +1,3 @@ +div { +height: 0; /* Comment */ +} diff --git a/test/options/always-semicolon/process/css/test-5.css b/test/options/always-semicolon/process/css/test-5.css new file mode 100644 index 00000000..578fd29c --- /dev/null +++ b/test/options/always-semicolon/process/css/test-5.css @@ -0,0 +1,4 @@ +div { +top: 1px; +height: 0 /* 1comment */ /* 2comment */ +} diff --git a/test/options/always-semicolon/process/css/test-5.expected.css b/test/options/always-semicolon/process/css/test-5.expected.css new file mode 100644 index 00000000..1869f051 --- /dev/null +++ b/test/options/always-semicolon/process/css/test-5.expected.css @@ -0,0 +1,4 @@ +div { +top: 1px; +height: 0; /* 1comment */ /* 2comment */ +} diff --git a/test/options/always-semicolon-less/condition-multiline.less b/test/options/always-semicolon/process/less/condition-multiline.less similarity index 63% rename from test/options/always-semicolon-less/condition-multiline.less rename to test/options/always-semicolon/process/less/condition-multiline.less index b3071171..e4213d2f 100644 --- a/test/options/always-semicolon-less/condition-multiline.less +++ b/test/options/always-semicolon/process/less/condition-multiline.less @@ -1,6 +1,6 @@ div { @color: tomato; - when (@color = tomato) { + div when (@color = tomato) { top: 0; } } diff --git a/test/options/always-semicolon/process/less/condition.less b/test/options/always-semicolon/process/less/condition.less new file mode 100644 index 00000000..5608abdc --- /dev/null +++ b/test/options/always-semicolon/process/less/condition.less @@ -0,0 +1 @@ +div { @color: tomato; div when (@color = tomato) { top: 0; } } diff --git a/test/options/always-semicolon-less/include-1-multiline.expected.less b/test/options/always-semicolon/process/less/include-1-multiline.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-1-multiline.expected.less rename to test/options/always-semicolon/process/less/include-1-multiline.expected.less diff --git a/test/options/always-semicolon-less/include-1-multiline.less b/test/options/always-semicolon/process/less/include-1-multiline.less similarity index 100% rename from test/options/always-semicolon-less/include-1-multiline.less rename to test/options/always-semicolon/process/less/include-1-multiline.less diff --git a/test/options/always-semicolon-less/include-1.expected.less b/test/options/always-semicolon/process/less/include-1.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-1.expected.less rename to test/options/always-semicolon/process/less/include-1.expected.less diff --git a/test/options/always-semicolon-less/include-1.less b/test/options/always-semicolon/process/less/include-1.less similarity index 100% rename from test/options/always-semicolon-less/include-1.less rename to test/options/always-semicolon/process/less/include-1.less diff --git a/test/options/always-semicolon-less/include-2-multiline.expected.less b/test/options/always-semicolon/process/less/include-2-multiline.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-2-multiline.expected.less rename to test/options/always-semicolon/process/less/include-2-multiline.expected.less diff --git a/test/options/always-semicolon-less/include-2-multiline.less b/test/options/always-semicolon/process/less/include-2-multiline.less similarity index 100% rename from test/options/always-semicolon-less/include-2-multiline.less rename to test/options/always-semicolon/process/less/include-2-multiline.less diff --git a/test/options/always-semicolon-less/include-2.expected.less b/test/options/always-semicolon/process/less/include-2.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-2.expected.less rename to test/options/always-semicolon/process/less/include-2.expected.less diff --git a/test/options/always-semicolon-less/include-2.less b/test/options/always-semicolon/process/less/include-2.less similarity index 100% rename from test/options/always-semicolon-less/include-2.less rename to test/options/always-semicolon/process/less/include-2.less diff --git a/test/options/always-semicolon-less/include-3-multiline.expected.less b/test/options/always-semicolon/process/less/include-3-multiline.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-3-multiline.expected.less rename to test/options/always-semicolon/process/less/include-3-multiline.expected.less diff --git a/test/options/always-semicolon-less/include-3-multiline.less b/test/options/always-semicolon/process/less/include-3-multiline.less similarity index 100% rename from test/options/always-semicolon-less/include-3-multiline.less rename to test/options/always-semicolon/process/less/include-3-multiline.less diff --git a/test/options/always-semicolon-less/include-3.expected.less b/test/options/always-semicolon/process/less/include-3.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-3.expected.less rename to test/options/always-semicolon/process/less/include-3.expected.less diff --git a/test/options/always-semicolon-less/include-3.less b/test/options/always-semicolon/process/less/include-3.less similarity index 100% rename from test/options/always-semicolon-less/include-3.less rename to test/options/always-semicolon/process/less/include-3.less diff --git a/test/options/always-semicolon-less/include-4-multiline.expected.less b/test/options/always-semicolon/process/less/include-4-multiline.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-4-multiline.expected.less rename to test/options/always-semicolon/process/less/include-4-multiline.expected.less diff --git a/test/options/always-semicolon-less/include-4-multiline.less b/test/options/always-semicolon/process/less/include-4-multiline.less similarity index 100% rename from test/options/always-semicolon-less/include-4-multiline.less rename to test/options/always-semicolon/process/less/include-4-multiline.less diff --git a/test/options/always-semicolon-less/include-4.expected.less b/test/options/always-semicolon/process/less/include-4.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-4.expected.less rename to test/options/always-semicolon/process/less/include-4.expected.less diff --git a/test/options/always-semicolon-less/include-4.less b/test/options/always-semicolon/process/less/include-4.less similarity index 100% rename from test/options/always-semicolon-less/include-4.less rename to test/options/always-semicolon/process/less/include-4.less diff --git a/test/options/always-semicolon-less/include-5-multiline.expected.less b/test/options/always-semicolon/process/less/include-5-multiline.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-5-multiline.expected.less rename to test/options/always-semicolon/process/less/include-5-multiline.expected.less diff --git a/test/options/always-semicolon-less/include-5-multiline.less b/test/options/always-semicolon/process/less/include-5-multiline.less similarity index 100% rename from test/options/always-semicolon-less/include-5-multiline.less rename to test/options/always-semicolon/process/less/include-5-multiline.less diff --git a/test/options/always-semicolon-less/include-5.expected.less b/test/options/always-semicolon/process/less/include-5.expected.less similarity index 100% rename from test/options/always-semicolon-less/include-5.expected.less rename to test/options/always-semicolon/process/less/include-5.expected.less diff --git a/test/options/always-semicolon-less/include-5.less b/test/options/always-semicolon/process/less/include-5.less similarity index 100% rename from test/options/always-semicolon-less/include-5.less rename to test/options/always-semicolon/process/less/include-5.less diff --git a/test/options/always-semicolon/process/sass/test.sass b/test/options/always-semicolon/process/sass/test.sass new file mode 100644 index 00000000..6284097f --- /dev/null +++ b/test/options/always-semicolon/process/sass/test.sass @@ -0,0 +1,7 @@ +a + color: tomato + top: 0 + + p + a: b + c:d diff --git a/test/options/always-semicolon-scss/block-include-multiline.scss b/test/options/always-semicolon/process/scss/block-include-multiline.scss similarity index 100% rename from test/options/always-semicolon-scss/block-include-multiline.scss rename to test/options/always-semicolon/process/scss/block-include-multiline.scss diff --git a/test/options/always-semicolon-scss/block-include.scss b/test/options/always-semicolon/process/scss/block-include.scss similarity index 100% rename from test/options/always-semicolon-scss/block-include.scss rename to test/options/always-semicolon/process/scss/block-include.scss diff --git a/test/options/always-semicolon-scss/block-value-multiline.scss b/test/options/always-semicolon/process/scss/block-value-multiline.scss similarity index 100% rename from test/options/always-semicolon-scss/block-value-multiline.scss rename to test/options/always-semicolon/process/scss/block-value-multiline.scss diff --git a/test/options/always-semicolon-scss/block-value.scss b/test/options/always-semicolon/process/scss/block-value.scss similarity index 100% rename from test/options/always-semicolon-scss/block-value.scss rename to test/options/always-semicolon/process/scss/block-value.scss diff --git a/test/options/always-semicolon-scss/condition-multiline.scss b/test/options/always-semicolon/process/scss/condition-multiline.scss similarity index 100% rename from test/options/always-semicolon-scss/condition-multiline.scss rename to test/options/always-semicolon/process/scss/condition-multiline.scss diff --git a/test/options/always-semicolon-scss/condition.scss b/test/options/always-semicolon/process/scss/condition.scss similarity index 100% rename from test/options/always-semicolon-scss/condition.scss rename to test/options/always-semicolon/process/scss/condition.scss diff --git a/test/options/always-semicolon-scss/extend-multiline.expected.scss b/test/options/always-semicolon/process/scss/extend-multiline.expected.scss similarity index 100% rename from test/options/always-semicolon-scss/extend-multiline.expected.scss rename to test/options/always-semicolon/process/scss/extend-multiline.expected.scss diff --git a/test/options/always-semicolon-scss/extend-multiline.scss b/test/options/always-semicolon/process/scss/extend-multiline.scss similarity index 100% rename from test/options/always-semicolon-scss/extend-multiline.scss rename to test/options/always-semicolon/process/scss/extend-multiline.scss diff --git a/test/options/always-semicolon-scss/extend.expected.scss b/test/options/always-semicolon/process/scss/extend.expected.scss similarity index 100% rename from test/options/always-semicolon-scss/extend.expected.scss rename to test/options/always-semicolon/process/scss/extend.expected.scss diff --git a/test/options/always-semicolon-scss/extend.scss b/test/options/always-semicolon/process/scss/extend.scss similarity index 100% rename from test/options/always-semicolon-scss/extend.scss rename to test/options/always-semicolon/process/scss/extend.scss diff --git a/test/options/always-semicolon-scss/include-1-multiline.expected.scss b/test/options/always-semicolon/process/scss/include-1-multiline.expected.scss similarity index 100% rename from test/options/always-semicolon-scss/include-1-multiline.expected.scss rename to test/options/always-semicolon/process/scss/include-1-multiline.expected.scss diff --git a/test/options/always-semicolon-scss/include-1-multiline.scss b/test/options/always-semicolon/process/scss/include-1-multiline.scss similarity index 100% rename from test/options/always-semicolon-scss/include-1-multiline.scss rename to test/options/always-semicolon/process/scss/include-1-multiline.scss diff --git a/test/options/always-semicolon-scss/include-1.expected.scss b/test/options/always-semicolon/process/scss/include-1.expected.scss similarity index 100% rename from test/options/always-semicolon-scss/include-1.expected.scss rename to test/options/always-semicolon/process/scss/include-1.expected.scss diff --git a/test/options/always-semicolon-scss/include-1.scss b/test/options/always-semicolon/process/scss/include-1.scss similarity index 100% rename from test/options/always-semicolon-scss/include-1.scss rename to test/options/always-semicolon/process/scss/include-1.scss diff --git a/test/options/always-semicolon-scss/include-2-multiline.expected.scss b/test/options/always-semicolon/process/scss/include-2-multiline.expected.scss similarity index 100% rename from test/options/always-semicolon-scss/include-2-multiline.expected.scss rename to test/options/always-semicolon/process/scss/include-2-multiline.expected.scss diff --git a/test/options/always-semicolon-scss/include-2-multiline.scss b/test/options/always-semicolon/process/scss/include-2-multiline.scss similarity index 100% rename from test/options/always-semicolon-scss/include-2-multiline.scss rename to test/options/always-semicolon/process/scss/include-2-multiline.scss diff --git a/test/options/always-semicolon-scss/include-2.expected.scss b/test/options/always-semicolon/process/scss/include-2.expected.scss similarity index 100% rename from test/options/always-semicolon-scss/include-2.expected.scss rename to test/options/always-semicolon/process/scss/include-2.expected.scss diff --git a/test/options/always-semicolon-scss/include-2.scss b/test/options/always-semicolon/process/scss/include-2.scss similarity index 100% rename from test/options/always-semicolon-scss/include-2.scss rename to test/options/always-semicolon/process/scss/include-2.scss diff --git a/test/options/always-semicolon-scss/loop-multiline.scss b/test/options/always-semicolon/process/scss/loop-multiline.scss similarity index 100% rename from test/options/always-semicolon-scss/loop-multiline.scss rename to test/options/always-semicolon/process/scss/loop-multiline.scss diff --git a/test/options/always-semicolon-scss/loop.scss b/test/options/always-semicolon/process/scss/loop.scss similarity index 100% rename from test/options/always-semicolon-scss/loop.scss rename to test/options/always-semicolon/process/scss/loop.scss diff --git a/test/options/always-semicolon/process/test.js b/test/options/always-semicolon/process/test.js new file mode 100644 index 00000000..7860c44c --- /dev/null +++ b/test/options/always-semicolon/process/test.js @@ -0,0 +1,171 @@ +let Test = require('../../option_test'); + +describe('Option `always-semicolon`, process', function() { + describe('css', function() { + it('Should add semicolon for last property if missing. Test 1', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('test-1.css', 'test-1.expected.css'); + }); + + it('Should add semicolon for last property if missing. Test 2', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('test-2.css', 'test-2.expected.css'); + }); + + it('Should add semicolon for last property if missing. Test 3', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('test-3.css', 'test-3.expected.css'); + }); + + it('Should add semicolon for last property if missing. Test 4', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('test-4.css', 'test-4.expected.css'); + }); + + it('Should add semicolon for last property if missing. Test 5', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('test-5.css', 'test-5.expected.css'); + }); + }); + + describe('less', function() { + it.skip('Should not add semicolon to condition (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('condition.less'); + }); + + it.skip('Should not add semicolon to condition (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('condition-multiline.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 1 (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-1.less', 'include-1.expected.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 1 (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-1-multiline.less', 'include-1-multiline.expected.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 2 (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-2.less', 'include-2.expected.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 2 (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-2-multiline.less', 'include-2-multiline.expected.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 3 (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-3.less', 'include-3.expected.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 3 (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-3-multiline.less', 'include-3-multiline.expected.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 4 (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-4.less', 'include-4.expected.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 4 (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-4-multiline.less', 'include-4-multiline.expected.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 5 (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-5.less', 'include-5.expected.less'); + }); + + it('Should add semicolon to last included mixin if missing. Test 5 (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-5-multiline.less', 'include-5-multiline.expected.less'); + }); + }); + + describe('sass', function() { + it('Should not add semicolon', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('test.sass'); + }); + }); + + describe('scss', function() { + it('Should not add semicolon if last value is block (singl-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('block-value.scss'); + }); + + it('Should not add semicolon if last value is block (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('block-value-multiline.scss'); + }); + + it('Should add semicolon to last included mixin if missing. Test 1 (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-1.scss', 'include-1.expected.scss'); + }); + + it('Should add semicolon to last included mixin if missing. Test 1 (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-1-multiline.scss', 'include-1-multiline.expected.scss'); + }); + + it('Should add semicolon to last included mixin if missing. Test 2 (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-2.scss', 'include-2.expected.scss'); + }); + + it('Should add semicolon to last included mixin if missing. Test 2 (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('include-2-multiline.scss', 'include-2-multiline.expected.scss'); + }); + + it('Should not add semicolon to last included mixin if there is a block (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('block-include.scss'); + }); + + it('Should not add semicolon to last included mixin if there is a block (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('block-include-multiline.scss'); + }); + + it('Should add semicolon to last extend if missing (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('extend.scss', 'extend.expected.scss'); + }); + + it('Should add semicolon to last extend if missing (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('extend-multiline.scss', 'extend-multiline.expected.scss'); + }); + + it('Should not add semicolon to condition (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('condition.scss'); + }); + + it('Should not add semicolon to condition (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('condition-multiline.scss'); + }); + + it('Should not add semicolon to loop (single-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('loop.scss'); + }); + + it('Should not add semicolon to loop (multi-line style)', function() { + let test = new Test(this, {'always-semicolon': true}); + return test.shouldBeEqual('loop-multiline.scss'); + }); + }); +}); diff --git a/test/options/block-indent.js b/test/options/block-indent.js deleted file mode 100644 index 4245d3d2..00000000 --- a/test/options/block-indent.js +++ /dev/null @@ -1,98 +0,0 @@ -var assert = require('assert'); - -describe('options/block-indent', function() { - it('Invalid Number value should not change space after brace', function() { - var input = 'a { color: red }'; - this.comb.configure({ 'block-indent': 3.5 }); - assert.equal(this.comb.processString(input), input); - }); - - it('Invalid String value should not change space after brace', function() { - var input = 'a { color: red }'; - this.comb.configure({ 'block-indent': 'foobar' }); - assert.equal(this.comb.processString(input), input); - }); - - it('Boolean value should not change space after brace', function() { - var input = ' \n a { color: red } @media all {.input__control { color: #000;\n}\n}'; - this.comb.configure({ 'block-indent': true }); - assert.equal(this.comb.processString(input), input); - }); - - it('Valid Number value should set equal space after brace', function() { - this.comb.configure({ 'block-indent': 3 }); - assert.equal( - this.comb.processString(' a\n{ color: red } @media all { .input__control\n{ color: #000;\n}\n}'), - 'a\n{ color: red \n}\n@media all {\n .input__control\n { color: #000;\n }\n}' - ); - }); - it('Valid String value should set equal space after brace', function() { - this.comb.configure({ 'block-indent': '\t' }); - assert.equal( - this.comb.processString(' a { color: red } @media all { .input__control\n{ color: #000;\n}\n}'), - 'a { color: red \n}\n@media all {\n\t.input__control\n\t{ color: #000;\n\t}\n}' - ); - }); - - it('Should detect the block-indent option set to four spaces', function() { - this.shouldDetect( - ['block-indent'], - ' \na { color: red \n}\n@media all {\n .input__control { color: #000;\n }\n}', - { - 'block-indent': ' ' - } - ); - }); - - it('Should detect the block-indent option set to three spaces', function() { - this.shouldDetect( - ['block-indent'], - 'a\n{ color: red \n}\n@media all {\n .input__control\n { color: #000;\n }\n}', - { - 'block-indent': ' ' - } - ); - }); - - it('Should detect the block-indent option set to a tab', function() { - this.shouldDetect( - ['block-indent'], - 'a { color: red \n}\n@media all {\n\t.input__control\n\t{ color: #000;\n\t}\n}', - { - 'block-indent': '\t' - } - ); - }); - it('Should detect the block-indent option set to an empty string', function() { - this.shouldDetect( - ['block-indent'], - 'a { color: red \n}\n@media all {\n.input__control\n{ color: #000;\n}\n}', - { - 'block-indent': '' - } - ); - }); - it('Should detect the block-indent option in a complex case', function() { - this.shouldDetect( - ['block-indent'], - 'a { color: red \n}\n@media all {\n\t.input__control\n\t{ color: #000;\n\t\n\t\n\t\n\t}\n}', - { - 'block-indent': '\t' - } - ); - }); - it('Should detect nothing with an empty block, test 1', function() { - this.shouldDetect( - ['block-indent'], - 'a{ }', - {} - ); - }); - it('Should detect nothing with an empty block, test 2', function() { - this.shouldDetect( - ['block-indent'], - 'a{}', - {} - ); - }); -}); diff --git a/test/options/block-indent/detect/css/test-3.expected.css b/test/options/block-indent/detect/css/test-3.expected.css new file mode 100644 index 00000000..684ff987 --- /dev/null +++ b/test/options/block-indent/detect/css/test-3.expected.css @@ -0,0 +1,36 @@ +a {color: tomato; top: 0; +} + +a { color: tomato; + top: 0; +} + +a { color: tomato; + top: 0; +} + +a { + color: tomato; + top: 0; +} + +a { + color: tomato; + top: 0; +} + +a { + color: tomato; + top: 0; +} + +@media print { a {color: tomato; top: 0; + } +} + +@media print { + a { + color: tomato; + top: 0; + } +} diff --git a/test/options/block-indent/detect/css/test.css b/test/options/block-indent/detect/css/test.css new file mode 100644 index 00000000..b1b52eb3 --- /dev/null +++ b/test/options/block-indent/detect/css/test.css @@ -0,0 +1,30 @@ +a {color: tomato; top: 0;} + +a { color: tomato; +top: 0; } + +a { color: tomato; + top: 0; } + +a { +color: tomato; +top: 0; } + +a { +color: tomato; +top: 0; +} + + a { + color: tomato; + top: 0; + } + +@media print { a {color: tomato; top: 0; } } + + @media print { +a { + color: tomato; + top: 0; + } + } diff --git a/test/options/block-indent/detect/test.js b/test/options/block-indent/detect/test.js new file mode 100644 index 00000000..6035c3d8 --- /dev/null +++ b/test/options/block-indent/detect/test.js @@ -0,0 +1,49 @@ +let Test = require('../../option_test'); + +describe('Option `block-indent`, detect', function() { + describe('css', function() { + it('Should detect nothing with an empty block, test 1', function() { + let test = new Test(this); + test.shouldDetect( + ['block-indent'], + 'a{ }', + {} + ); + }); + + it('Should detect nothing with an empty block, test 2', function() { + let test = new Test(this); + test.shouldDetect( + ['block-indent'], + 'a{}', + {} + ); + }); + + it('Should detect correct number of spaces', function() { + let test = new Test(this); + test.shouldDetect( + ['block-indent'], + 'a{\n top: 0;\n color: tomato;\n}', + {'block-indent': ' '} + ); + }); + + it('Should detect no indent for one-line code', function() { + let test = new Test(this); + test.shouldDetect( + ['block-indent'], + 'a{ top: 0; color: tomato; }', + {} + ); + }); + + it('Valid string value => should set proper space after combnator', function() { + let test = new Test(this, { + 'block-indent': ' ', + 'space-before-closing-brace': '\n' + }); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + }); +}); diff --git a/test/options/block-indent/lint/test.js b/test/options/block-indent/lint/test.js new file mode 100644 index 00000000..231e4209 --- /dev/null +++ b/test/options/block-indent/lint/test.js @@ -0,0 +1,4 @@ +describe('Option `block-indent`, lint', function() { + describe('css', function() { + }); +}); diff --git a/test/options/block-indent/process/css/issue-379.css b/test/options/block-indent/process/css/issue-379.css new file mode 100644 index 00000000..46ef2f74 --- /dev/null +++ b/test/options/block-indent/process/css/issue-379.css @@ -0,0 +1,3 @@ +.pageArea #header { +color: red; +} diff --git a/test/options/block-indent/process/css/issue-379.expected.css b/test/options/block-indent/process/css/issue-379.expected.css new file mode 100644 index 00000000..3708595d --- /dev/null +++ b/test/options/block-indent/process/css/issue-379.expected.css @@ -0,0 +1,3 @@ +.pageArea #header { + color: red; + } diff --git a/test/options/block-indent/process/css/test-2.expected.css b/test/options/block-indent/process/css/test-2.expected.css new file mode 100644 index 00000000..467a9326 --- /dev/null +++ b/test/options/block-indent/process/css/test-2.expected.css @@ -0,0 +1,30 @@ +a {color: tomato; top: 0;} + +a { color: tomato; + top: 0; } + +a { color: tomato; + top: 0; } + +a { + color: tomato; + top: 0; } + +a { + color: tomato; + top: 0; + } + +a { + color: tomato; + top: 0; + } + +@media print { a {color: tomato; top: 0; } } + +@media print { + a { + color: tomato; + top: 0; + } + } diff --git a/test/options/block-indent/process/css/test-3.expected.css b/test/options/block-indent/process/css/test-3.expected.css new file mode 100644 index 00000000..684ff987 --- /dev/null +++ b/test/options/block-indent/process/css/test-3.expected.css @@ -0,0 +1,36 @@ +a {color: tomato; top: 0; +} + +a { color: tomato; + top: 0; +} + +a { color: tomato; + top: 0; +} + +a { + color: tomato; + top: 0; +} + +a { + color: tomato; + top: 0; +} + +a { + color: tomato; + top: 0; +} + +@media print { a {color: tomato; top: 0; + } +} + +@media print { + a { + color: tomato; + top: 0; + } +} diff --git a/test/options/block-indent/process/css/test.css b/test/options/block-indent/process/css/test.css new file mode 100644 index 00000000..b1b52eb3 --- /dev/null +++ b/test/options/block-indent/process/css/test.css @@ -0,0 +1,30 @@ +a {color: tomato; top: 0;} + +a { color: tomato; +top: 0; } + +a { color: tomato; + top: 0; } + +a { +color: tomato; +top: 0; } + +a { +color: tomato; +top: 0; +} + + a { + color: tomato; + top: 0; + } + +@media print { a {color: tomato; top: 0; } } + + @media print { +a { + color: tomato; + top: 0; + } + } diff --git a/test/options/block-indent/process/css/test.expected.css b/test/options/block-indent/process/css/test.expected.css new file mode 100644 index 00000000..0c684ec8 --- /dev/null +++ b/test/options/block-indent/process/css/test.expected.css @@ -0,0 +1,30 @@ +a {color: tomato; top: 0;} + +a { color: tomato; +top: 0; } + +a { color: tomato; +top: 0; } + +a { +color: tomato; +top: 0; } + +a { +color: tomato; +top: 0; +} + +a { +color: tomato; +top: 0; +} + +@media print { a {color: tomato; top: 0; } } + +@media print { +a { +color: tomato; +top: 0; +} +} diff --git a/test/options/block-indent/process/sass/block.expected.sass b/test/options/block-indent/process/sass/block.expected.sass new file mode 100644 index 00000000..ddae383f --- /dev/null +++ b/test/options/block-indent/process/sass/block.expected.sass @@ -0,0 +1,6 @@ +a + color: tomato + top: 0 + +p + bottom: 0 diff --git a/test/options/block-indent/process/sass/block.sass b/test/options/block-indent/process/sass/block.sass new file mode 100644 index 00000000..9be6816f --- /dev/null +++ b/test/options/block-indent/process/sass/block.sass @@ -0,0 +1,6 @@ +a + color: tomato + top: 0 + +p + bottom: 0 diff --git a/test/options/block-indent/process/sass/mixin.expected.sass b/test/options/block-indent/process/sass/mixin.expected.sass new file mode 100644 index 00000000..f9351138 --- /dev/null +++ b/test/options/block-indent/process/sass/mixin.expected.sass @@ -0,0 +1,8 @@ +@mixin sexy-border($color, $width: 1px) + border: + color: $color + width: $width + style: dashed + +p + +sexy-border(blue) diff --git a/test/options/block-indent/process/sass/mixin.sass b/test/options/block-indent/process/sass/mixin.sass new file mode 100644 index 00000000..d25771d9 --- /dev/null +++ b/test/options/block-indent/process/sass/mixin.sass @@ -0,0 +1,8 @@ +@mixin sexy-border($color, $width: 1px) + border: + color: $color + width: $width + style: dashed + +p + +sexy-border(blue) diff --git a/test/options/block-indent/process/sass/nested-ruleset.expected.sass b/test/options/block-indent/process/sass/nested-ruleset.expected.sass new file mode 100644 index 00000000..2c7edc6c --- /dev/null +++ b/test/options/block-indent/process/sass/nested-ruleset.expected.sass @@ -0,0 +1,9 @@ +a + color: tomato + top: 0 + + p + bottom: 0 + + &:hover + color: yellow diff --git a/test/options/block-indent/process/sass/nested-ruleset.sass b/test/options/block-indent/process/sass/nested-ruleset.sass new file mode 100644 index 00000000..8567d2e5 --- /dev/null +++ b/test/options/block-indent/process/sass/nested-ruleset.sass @@ -0,0 +1,9 @@ +a + color: tomato + top: 0 + + p + bottom: 0 + + &:hover + color: yellow diff --git a/test/options/block-indent/process/scss/nested-include.expected.scss b/test/options/block-indent/process/scss/nested-include.expected.scss new file mode 100644 index 00000000..c419883d --- /dev/null +++ b/test/options/block-indent/process/scss/nested-include.expected.scss @@ -0,0 +1,5 @@ +li{ + @include respond-to(mobile){ float: none; } + float: left; + width: 25%; + } diff --git a/test/options/block-indent/process/scss/nested-include.scss b/test/options/block-indent/process/scss/nested-include.scss new file mode 100644 index 00000000..93504be6 --- /dev/null +++ b/test/options/block-indent/process/scss/nested-include.scss @@ -0,0 +1,5 @@ +li{ + @include respond-to(mobile){ float: none; } + float: left; + width: 25%; +} diff --git a/test/options/block-indent/process/test.js b/test/options/block-indent/process/test.js new file mode 100644 index 00000000..3f61e4a6 --- /dev/null +++ b/test/options/block-indent/process/test.js @@ -0,0 +1,59 @@ +let Test = require('../../option_test'); + +describe('Option `block-indent`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'block-indent': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'block-indent': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'block-indent': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper number of spaces', function() { + let test = new Test(this, {'block-indent': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value => should set proper number of spaces', function() { + let test = new Test(this, {'block-indent': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Issue 379', function() { + let test = new Test(this, {'block-indent': 4}); + return test.shouldBeEqual('issue-379.css', 'issue-379.expected.css'); + }); + }); + + describe('sass', function() { + it('First level ruleset\'s block', function() { + let test = new Test(this, {'block-indent': 2}); + return test.shouldBeEqual('block.sass', 'block.expected.sass'); + }); + + it('Nested ruleset', function() { + let test = new Test(this, {'block-indent': 2}); + return test.shouldBeEqual('nested-ruleset.sass', 'nested-ruleset.expected.sass'); + }); + + it('Mixin', function() { + let test = new Test(this, {'block-indent': 4}); + return test.shouldBeEqual('mixin.sass', 'mixin.expected.sass'); + }); + }); + + describe('scss', function() { + it('Issue 213', function() { + let test = new Test(this, {'block-indent': 2}); + return test.shouldBeEqual('nested-include.scss', 'nested-include.expected.scss'); + }); + }); +}); diff --git a/test/options/colon-space.js b/test/options/colon-space.js deleted file mode 100644 index b13c1cf2..00000000 --- a/test/options/colon-space.js +++ /dev/null @@ -1,121 +0,0 @@ -var assert = require('assert'); - -describe('options/colon-space', function() { - it('String value should not change space around colon', function() { - var input = 'a { color : red }'; - this.comb.configure({ 'colon-space': ' ' }); - assert.equal(this.comb.processString(input), input); - }); - - it('Boolean value should not change space around colon', function() { - var input = 'a { color : red }'; - this.comb.configure({ 'colon-space': true }); - assert.equal(this.comb.processString(input), input); - }); - - it('Array of strings should set proper space around colon', function() { - this.comb.configure({ 'colon-space': ['', ' '] }); - assert.equal( - this.comb.processString( - 'a { color: red }' + - 'a{color:red}' + - 'a {color : red}' + - 'a {color : /* foo */ red }' + - 'a {color /* bar */ : red }' - ), - 'a { color: red }' + - 'a{color: red}' + - 'a {color: red}' + - 'a {color: /* foo */ red }' + - 'a {color /* bar */: red }' - ); - }); - - it('Array of numbers should set proper space around colon', function() { - this.comb.configure({ 'colon-space': [0, 1] }); - assert.equal( - this.comb.processString( - 'a { color: red }' + - 'a{color:red}' + - 'a {color : red}' + - 'a {color : /* foo */ red }' + - 'a {color /* bar */ : red }' - ), - 'a { color: red }' + - 'a{color: red}' + - 'a {color: red}' + - 'a {color: /* foo */ red }' + - 'a {color /* bar */: red }' - ); - }); - - it('Should detect no whitespaces around colon', function() { - this.shouldDetect( - ['colon-space'], - 'a { color:red }', - { - 'colon-space': ['', ''] - } - ); - }); - - it('Should detect space after colon', function() { - this.shouldDetect( - ['colon-space'], - 'a { color: red }', - { - 'colon-space': ['', ' '] - } - ); - }); - - it('Should detect space after colon from two variants.', function() { - this.shouldDetect( - ['colon-space'], - 'a { color: red; color:red }', - { - 'colon-space': ['', ' '] - } - ); - }); - - it('Should detect no whitespaces around colon along three variants', function() { - this.shouldDetect( - ['colon-space'], - 'a { color: red; background:red } b { width:10px }', - { - 'colon-space': ['', ''] - } - ); - }); - - it('Should detect space around colon', function() { - this.shouldDetect( - ['colon-space'], - 'a { color : red; background :red } b { width:10px }', - { - 'colon-space': [' ', ' '] - } - ); - }); - - it('Should detect different whitespaces around colon', function() { - this.shouldDetect( - ['colon-space'], - 'a { color : red; background :red } b { width : 10px }', - { - 'colon-space': [' ', ' '] - } - ); - }); - - it('Should detect whitespace after colon', function() { - this.shouldDetect( - ['colon-space'], - '.input\n{\n position: relative;\n\n display: inline-block;\n\n width: 100%;\n}', - { - 'colon-space': ['', ' '] - } - ); - }); -}); diff --git a/test/options/color-case.js b/test/options/color-case.js deleted file mode 100644 index e94af2ff..00000000 --- a/test/options/color-case.js +++ /dev/null @@ -1,102 +0,0 @@ -var assert = require('assert'); - -describe('options/color-case', function() { - it('Should switch colors to upper case', function() { - this.comb.configure({ 'color-case': 'upper' }); - assert.equal( - this.comb.processString( - 'div { color: #fff }' - ), - 'div { color: #FFF }' - ); - }); - - it('Should switch colors to lower case', function() { - this.comb.configure({ 'color-case': 'lower' }); - assert.equal( - this.comb.processString( - 'div { color: #FFF }' - ), - 'div { color: #fff }' - ); - }); - - it('Should switch color-case in complex rules', function() { - this.comb.configure({ 'color-case': 'lower' }); - assert.equal( - this.comb.processString( - 'div { background: url(img.png#RND) #E3E3E3 0 100% no-repeat;' + - ' box-shadow: 1px 2px 3px 4px #F0F0F0 inset; }' - ), - 'div { background: url(img.png#RND) #e3e3e3 0 100% no-repeat;' + - ' box-shadow: 1px 2px 3px 4px #f0f0f0 inset; }' - ); - }); - - it('Should not switch selector case', function() { - this.comb.configure({ 'color-case': 'lower' }); - assert.equal( - this.comb.processString( - '#Header { color: #FFF }' - ), - '#Header { color: #fff }' - ); - }); - - it('Should detect uppercase color', function() { - this.shouldDetect( - ['color-case'], - 'a { color: #F3F3F3 }', - { - 'color-case': 'upper' - } - ); - }); - - it('Should detect lowercase color', function() { - this.shouldDetect( - ['color-case'], - 'a { color: #f6f6f6 }', - { - 'color-case': 'lower' - } - ); - }); - - it('Should detect uppercase color in a shorthand', function() { - this.shouldDetect( - ['color-case'], - 'a { color: #FFF }', - { - 'color-case': 'upper' - } - ); - }); - - it('Should detect lowercase color in a shorthand', function() { - this.shouldDetect( - ['color-case'], - 'a { color: #fff }', - { - 'color-case': 'lower' - } - ); - }); - - it('Shouldn’t detect color case if it contains only digits', function() { - this.shouldDetect( - ['color-case'], - 'a { color: #333 }', - {} - ); - }); - - it('Shouldn’t detect color case if it is in mixed case', function() { - this.shouldDetect( - ['color-case'], - 'a { color: #fFfFfF }', - {} - ); - }); - -}); diff --git a/test/options/color-case/detect/test.js b/test/options/color-case/detect/test.js new file mode 100644 index 00000000..cc075c37 --- /dev/null +++ b/test/options/color-case/detect/test.js @@ -0,0 +1,59 @@ +let Test = require('../../option_test'); + +describe('Option `block-indent`, detect', function() { + describe('css', function() { + it('Should detect uppercase color', function() { + let test = new Test(this); + test.shouldDetect( + ['color-case'], + 'a { color: #F3F3F3 }', + {'color-case': 'upper'} + ); + }); + + it('Should detect lowercase color', function() { + let test = new Test(this); + test.shouldDetect( + ['color-case'], + 'a { color: #f6f6f6 }', + {'color-case': 'lower'} + ); + }); + + it('Should detect uppercase color in a shorthand', function() { + let test = new Test(this); + test.shouldDetect( + ['color-case'], + 'a { color: #FFF }', + {'color-case': 'upper'} + ); + }); + + it('Should detect lowercase color in a shorthand', function() { + let test = new Test(this); + test.shouldDetect( + ['color-case'], + 'a { color: #fff }', + {'color-case': 'lower'} + ); + }); + + it('Shouldn’t detect color case if it contains only digits', function() { + let test = new Test(this); + test.shouldDetect( + ['color-case'], + 'a { color: #333 }', + {} + ); + }); + + it('Shouldn’t detect color case if it is in mixed case', function() { + let test = new Test(this); + test.shouldDetect( + ['color-case'], + 'a { color: #fFfFfF }', + {} + ); + }); + }); +}); diff --git a/test/options/color-case/lint/test.js b/test/options/color-case/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/color-case/process/test.js b/test/options/color-case/process/test.js new file mode 100644 index 00000000..3b8ae697 --- /dev/null +++ b/test/options/color-case/process/test.js @@ -0,0 +1,40 @@ +let assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `color-case`, process', function() { + describe('css', function() { + it('Should switch colors to upper case', function() { + let test = new Test(this, {'color-case': 'upper'}); + return test.comb.processString('div { color: #fff }') + .then(actual => { + assert.equal(actual, 'div { color: #FFF }'); + }); + }); + + it('Should switch colors to lower case', function() { + let test = new Test(this, {'color-case': 'lower'}); + return test.comb.processString('div { color: #FFF }') + .then(actual => { + assert.equal(actual, 'div { color: #fff }'); + }); + }); + + it('Should switch color-case in complex rules', function() { + let test = new Test(this, {'color-case': 'lower'}); + return test.comb.processString( + 'div { background: url(img.png#RND) #E3E3E3 0 100% no-repeat;' + + ' box-shadow: 1px 2px 3px 4px #F0F0F0 inset; }' + ).then(actual => { + assert.equal(actual, 'div { background: url(img.png#RND) #e3e3e3 0 100% no-repeat; box-shadow: 1px 2px 3px 4px #f0f0f0 inset; }'); + }); + }); + + it('Should not switch selector case', function() { + let test = new Test(this, {'color-case': 'lower'}); + return test.comb.processString('#Header { color: #FFF }') + .then(function(actual) { + assert.equal(actual, '#Header { color: #fff }'); + }); + }); + }); +}); diff --git a/test/options/color-shorthand.js b/test/options/color-shorthand.js deleted file mode 100644 index d1708c0c..00000000 --- a/test/options/color-shorthand.js +++ /dev/null @@ -1,70 +0,0 @@ -var assert = require('assert'); - -describe('options/color-shorthand', function() { - it('Should shrink hexadecimal colors to 3 symbols', function() { - this.comb.configure({ 'color-shorthand': true }); - assert.equal( - this.comb.processString( - 'div { color: #aabbcc }' - ), - 'div { color: #abc }' - ); - }); - - it('Should expand hexadecimal colors to 6 symbols', function() { - this.comb.configure({ 'color-shorthand': false }); - assert.equal( - this.comb.processString( - 'div { color: #7ad }' - ), - 'div { color: #77aadd }' - ); - }); - - it('Should save case while processing', function() { - this.comb.configure({ 'color-shorthand': true }); - assert.equal( - this.comb.processString( - 'div { color: #fFAafF }' - ), - 'div { color: #fAf }' - ); - }); - - - it('Should detect non-shorthanded color', function() { - this.shouldDetect( - ['color-shorthand'], - 'a { color: #FF33EE }', - { - 'color-shorthand': false - } - ); - }); - - it('Should detect shorthanded color', function() { - this.shouldDetect( - ['color-shorthand'], - 'a { color: #fff }', - { - 'color-shorthand': true - } - ); - }); - - it('Shouldn’t detect if a color is shorthanded if it can’t be shorthanded', function() { - this.shouldDetect( - ['color-shorthand'], - 'a { color: #F3F3F3 }', - {} - ); - }); - - it('Shouldn’t detect if a color is shorthanded if it is not a vhash', function() { - this.shouldDetect( - ['color-shorthand'], - 'a { color: rgba(0,0,0,0.5) }', - {} - ); - }); -}); diff --git a/test/options/color-shorthand/detect/css b/test/options/color-shorthand/detect/css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/color-shorthand/detect/test.js b/test/options/color-shorthand/detect/test.js new file mode 100644 index 00000000..59e46933 --- /dev/null +++ b/test/options/color-shorthand/detect/test.js @@ -0,0 +1,41 @@ +let Test = require('../../option_test'); + +describe('Option `color-shorthand`, detect', function() { + describe('css', function() { + it('Should detect non-shorthanded color', function() { + let test = new Test(this); + test.shouldDetect( + ['color-shorthand'], + 'a { color: #FF33EE }', + {'color-shorthand': false} + ); + }); + + it('Should detect shorthanded color', function() { + let test = new Test(this); + test.shouldDetect( + ['color-shorthand'], + 'a { color: #fff }', + {'color-shorthand': true} + ); + }); + + it('Shouldn’t detect if a color is shorthanded if it can’t be shorthanded', function() { + let test = new Test(this); + test.shouldDetect( + ['color-shorthand'], + 'a { color: #F3F3F3 }', + {} + ); + }); + + it('Shouldn’t detect if a color is shorthanded if it is not a vhash', function() { + let test = new Test(this); + test.shouldDetect( + ['color-shorthand'], + 'a { color: rgba(0,0,0,0.5) }', + {} + ); + }); + }); +}); diff --git a/test/options/color-shorthand/lint/css b/test/options/color-shorthand/lint/css new file mode 100644 index 00000000..e69de29b diff --git a/test/options/color-shorthand/lint/test.js b/test/options/color-shorthand/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/color-shorthand/process/test.js b/test/options/color-shorthand/process/test.js new file mode 100644 index 00000000..b9b1be7a --- /dev/null +++ b/test/options/color-shorthand/process/test.js @@ -0,0 +1,33 @@ +var assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `color-shorthand`, process', function() { + describe('css', function() { + it('Should shrink hexadecimal colors to 3 symbols', function() { + let test = new Test(this, {'color-shorthand': true}); + return test.comb.processString( + 'div { color: #aabbcc }' + ).then(function(actual) { + assert.equal(actual, 'div { color: #abc }'); + }); + }); + + it('Should expand hexadecimal colors to 6 symbols', function() { + let test = new Test(this, {'color-shorthand': false}); + return test.comb.processString( + 'div { color: #7ad }' + ).then(function(actual) { + assert.equal(actual, 'div { color: #77aadd }'); + }); + }); + + it('Should save case while processing', function() { + let test = new Test(this, {'color-shorthand': true}); + return test.comb.processString( + 'div { color: #fFAafF }' + ).then(function(actual) { + assert.equal(actual, 'div { color: #fAf }'); + }); + }); + }); +}); diff --git a/test/options/combinator-space.js b/test/options/combinator-space.js deleted file mode 100644 index f27ca386..00000000 --- a/test/options/combinator-space.js +++ /dev/null @@ -1,145 +0,0 @@ -var assert = require('assert'); - -describe('options/combinator-space', function() { - it('Number value should not change space around combinator', function() { - var input = 'a >b { color: red }'; - this.comb.configure({ 'combinator-space': 2 }); - assert.equal(this.comb.processString(input), input); - }); - - it('String value should not change space around combinator', function() { - var input = 'a >b { color: red }'; - this.comb.configure({ 'combinator-space': 'foobar' }); - assert.equal(this.comb.processString(input), input); - }); - - it('Boolean value should not change space around combinator', function() { - var input = 'a >b { color: red }'; - this.comb.configure({ 'combinator-space': true }); - assert.equal(this.comb.processString(input), input); - }); - - it('Array of strings should set proper spaces around combinator', function() { - this.comb.configure({ 'combinator-space': [' ', '\n'] }); - assert.equal( - this.comb.processString( - 'a>b { color: red }' + - 'a> b { color: red }' + - 'a >b { color: red }' + - 'a+b { color: red }' + - 'a+ b { color: red }' + - 'a +b { color: red }' + - 'a~b { color: red }' + - 'a~ b { color: red }' + - 'a ~b { color: red }' + - 'a ~b+ c>d { color: red }' - ), - 'a >\nb { color: red }' + - 'a >\nb { color: red }' + - 'a >\nb { color: red }' + - 'a +\nb { color: red }' + - 'a +\nb { color: red }' + - 'a +\nb { color: red }' + - 'a ~\nb { color: red }' + - 'a ~\nb { color: red }' + - 'a ~\nb { color: red }' + - 'a ~\nb +\nc >\nd { color: red }' - ); - }); - - it('Array of numbers should set proper spaces around combinator', function() { - this.comb.configure({ 'combinator-space': [0, 1] }); - assert.equal( - this.comb.processString( - 'a>b { color: red }' + - 'a> b { color: red }' + - 'a >b { color: red }' + - 'a+b { color: red }' + - 'a+ b { color: red }' + - 'a +b { color: red }' + - 'a~b { color: red }' + - 'a~ b { color: red }' + - 'a ~b { color: red }' + - 'a ~b+ c>d { color: red }' - ), - 'a> b { color: red }' + - 'a> b { color: red }' + - 'a> b { color: red }' + - 'a+ b { color: red }' + - 'a+ b { color: red }' + - 'a+ b { color: red }' + - 'a~ b { color: red }' + - 'a~ b { color: red }' + - 'a~ b { color: red }' + - 'a~ b+ c> d { color: red }' - ); - }); - - it('Should detect no whitespaces around combinator', function() { - this.shouldDetect( - ['combinator-space'], - 'a+b { color:red }', - { - 'combinator-space': ['', ''] - } - ); - }); - - it('Should detect a space around combinator', function() { - this.shouldDetect( - ['combinator-space'], - 'a + b { color:red }', - { - 'combinator-space': [' ', ' '] - } - ); - }); - - it('Should detect a mixed spaces around combinator', function() { - this.shouldDetect( - ['combinator-space'], - 'a + \n b { color:red }', - { - 'combinator-space': [' ', ' \n '] - } - ); - }); - - it('Should detect a space around combinator in long selector', function() { - this.shouldDetect( - ['combinator-space'], - 'a + b ~ c>d { color:red }', - { - 'combinator-space': [' ', ' '] - } - ); - }); - - it('Should detect a space around combinator in long selector, test 2', function() { - this.shouldDetect( - ['combinator-space'], - 'a>b + c + d { color:red }', - { - 'combinator-space': [' ', ' '] - } - ); - }); - - it('Should detect no whitespaces around combinator in long selector', function() { - this.shouldDetect( - ['combinator-space'], - 'a+b ~ c+d { color:red }', - { - 'combinator-space': ['', ''] - } - ); - }); - - it('Shouldn’t detect whitespaces around combinator in selector without combinators', function() { - this.shouldDetect( - ['combinator-space'], - 'a { color:red }', - {} - ); - }); -}); diff --git a/test/options/element-case.js b/test/options/element-case.js deleted file mode 100644 index fb95287c..00000000 --- a/test/options/element-case.js +++ /dev/null @@ -1,99 +0,0 @@ -var assert = require('assert'); - -describe('options/element-case', function() { - it('Invalid String should not change case of elements', function() { - this.comb.configure({ 'element-case': 'foobar' }); - assert.equal( - this.comb.processString( - 'LI a { color : red }' - ), - 'LI a { color : red }' - ); - }); - - it('Should switch tag name to upper case', function() { - this.comb.configure({ 'element-case': 'upper' }); - assert.equal( - this.comb.processString( - 'div { color: #fff }' - ), - 'DIV { color: #fff }' - ); - }); - - it('Should switch tag name to lower case', function() { - this.comb.configure({ 'element-case': 'lower' }); - assert.equal( - this.comb.processString( - 'DIV { color: #FFF }' - ), - 'div { color: #FFF }' - ); - }); - - it('Should switch element-case in complex rules', function() { - this.comb.configure({ 'element-case': 'lower' }); - assert.equal( - this.comb.processString( - 'UL > LI > .foo:not(A) { color: red }' - ), - 'ul > li > .foo:not(a) { color: red }' - ); - }); - - it('Should detect lowercase elements', function() { - this.shouldDetect( - ['element-case'], - 'a { color: red }', - { - 'element-case': 'lower' - } - ); - }); - - it('Should detect uppercase elements', function() { - this.shouldDetect( - ['element-case'], - 'A { color: red }', - { - 'element-case': 'upper' - } - ); - }); - - it('Should detect lowercase elements in a long selector', function() { - this.shouldDetect( - ['element-case'], - 'ul li:not(:hover) A { color: red }', - { - 'element-case': 'lower' - } - ); - }); - - it('Should detect uppercase elements in a long selector', function() { - this.shouldDetect( - ['element-case'], - 'ul .lol:not(LI) A { color: red }', - { - 'element-case': 'upper' - } - ); - }); - - it('Shouldn’t detect case of elements in a mixed case', function() { - this.shouldDetect( - ['element-case'], - 'aRtIcLe { color: red }', - {} - ); - }); - - it('Shouldn’t detect case of elements when there are no such', function() { - this.shouldDetect( - ['element-case'], - '*.lol { color: red }', - {} - ); - }); -}); diff --git a/test/options/element-case/detect/test.js b/test/options/element-case/detect/test.js new file mode 100644 index 00000000..22cc8bbf --- /dev/null +++ b/test/options/element-case/detect/test.js @@ -0,0 +1,59 @@ +let Test = require('../../option_test'); + +describe('Option `element-case`, detect', function() { + describe('css', function() { + it('Should detect lowercase elements', function() { + let test = new Test(this); + test.shouldDetect( + ['element-case'], + 'a { color: red }', + {'element-case': 'lower'} + ); + }); + + it('Should detect uppercase elements', function() { + let test = new Test(this); + test.shouldDetect( + ['element-case'], + 'A { color: red }', + {'element-case': 'upper'} + ); + }); + + it('Should detect lowercase elements in a long selector', function() { + let test = new Test(this); + test.shouldDetect( + ['element-case'], + 'ul li:not(:hover) A { color: red }', + {'element-case': 'lower'} + ); + }); + + it('Should detect uppercase elements in a long selector', function() { + let test = new Test(this); + test.shouldDetect( + ['element-case'], + 'ul .lol:not(LI) A { color: red }', + {'element-case': 'upper'} + ); + }); + + it('Shouldn’t detect case of elements in a mixed case', function() { + let test = new Test(this); + test.shouldDetect( + ['element-case'], + 'aRtIcLe { color: red }', + {} + ); + }); + + it('Shouldn’t detect case of elements when there are no such', function() { + let test = new Test(this); + test.shouldDetect( + ['element-case'], + '*.lol { color: red }', + {} + ); + }); + }); +}); diff --git a/test/options/element-case/lint/test.js b/test/options/element-case/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/element-case/process/scss/mixin.expected.scss b/test/options/element-case/process/scss/mixin.expected.scss new file mode 100644 index 00000000..8700a0ea --- /dev/null +++ b/test/options/element-case/process/scss/mixin.expected.scss @@ -0,0 +1,3 @@ +a { + @include boxSizingContent(); +} diff --git a/test/options/element-case/process/scss/mixin.scss b/test/options/element-case/process/scss/mixin.scss new file mode 100644 index 00000000..d2b5c33e --- /dev/null +++ b/test/options/element-case/process/scss/mixin.scss @@ -0,0 +1,3 @@ +A { + @include boxSizingContent(); +} diff --git a/test/options/element-case/process/test.js b/test/options/element-case/process/test.js new file mode 100644 index 00000000..350970fe --- /dev/null +++ b/test/options/element-case/process/test.js @@ -0,0 +1,49 @@ +var assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `element-case`, process', function() { + describe('css', function() { + it('Invalid String should not change case of elements', function() { + let test = new Test(this, {'element-case': 'foobar'}); + return test.comb.processString( + 'LI a { color : red }' + ).then(function(actual) { + assert.equal(actual, 'LI a { color : red }'); + }); + }); + + it('Should switch tag name to upper case', function() { + let test = new Test(this, {'element-case': 'upper'}); + return test.comb.processString( + 'div { color: #fff }' + ).then(function(actual) { + assert.equal(actual, 'DIV { color: #fff }'); + }); + }); + + it('Should switch tag name to lower case', function() { + let test = new Test(this, {'element-case': 'lower'}); + return test.comb.processString( + 'DIV { color: #FFF }' + ).then(function(actual) { + assert.equal(actual, 'div { color: #FFF }'); + }); + }); + + it('Should switch element-case in complex rules', function() { + let test = new Test(this, {'element-case': 'lower'}); + return test.comb.processString( + 'UL > LI > .foo:not(A) { color: red }' + ).then(function(actual) { + assert.equal(actual, 'ul > li > .foo:not(a) { color: red }'); + }); + }); + }); + + describe('scss', function() { + it('Should not touch mixin names', function() { + let test = new Test(this, {'element-case': 'lower'}); + return test.shouldBeEqual('mixin.scss', 'mixin.expected.scss'); + }); + }); +}); diff --git a/test/options/eof-newline.js b/test/options/eof-newline.js deleted file mode 100644 index ead5fcf1..00000000 --- a/test/options/eof-newline.js +++ /dev/null @@ -1,69 +0,0 @@ -var assert = require('assert'); - -describe('options/eof-newline', function() { - it('Invalid value should not change trim trailing brac', function() { - this.comb.configure({ 'eof-newline': 'foobar' }); - assert.equal( - this.comb.processString('a { color: red } \n'), - 'a { color: red } \n' - ); - }); - it('Boolean true value should insert line-break at eof', function() { - this.comb.configure({ 'eof-newline': true }); - assert.equal( - this.comb.processString( - 'a {color:red} ' - ), - 'a {color:red} \n' - ); - }); - it('Boolean false value should remove line-break from eof', function() { - this.comb.configure({ 'eof-newline': false }); - assert.equal( - this.comb.processString( - 'a {color:red} \n' - ), - 'a {color:red} ' - ); - }); - - it('Shouldn’t detect eof newline', function() { - this.shouldDetect( - ['eof-newline'], - 'a { color: red }', - { - 'eof-newline': false - } - ); - }); - - it('Should detect eof newline', function() { - this.shouldDetect( - ['eof-newline'], - 'a { color: red }\n', - { - 'eof-newline': true - } - ); - }); - - it('Shouldn’t detect eof newline with spaces at the end', function() { - this.shouldDetect( - ['eof-newline'], - 'a { color: red } ', - { - 'eof-newline': false - } - ); - }); - - it('Should detect eof newline with mixed spaces at the end', function() { - this.shouldDetect( - ['eof-newline'], - 'a { color: red } \n ', - { - 'eof-newline': true - } - ); - }); -}); diff --git a/test/options/eof-newline/detect/test.js b/test/options/eof-newline/detect/test.js new file mode 100644 index 00000000..c7c6cd80 --- /dev/null +++ b/test/options/eof-newline/detect/test.js @@ -0,0 +1,42 @@ +let Test = require('../../option_test'); + +describe('Option `eof-newline`, detect', function() { + describe('css', function() { + it('Shouldn’t detect eof newline', function() { + let test = new Test(this); + test.shouldDetect( + ['eof-newline'], + 'a { color: red }', + {'eof-newline': false} + ); + }); + + it('Should detect eof newline', function() { + let test = new Test(this); + test.shouldDetect( + ['eof-newline'], + 'a { color: red }\n', + {'eof-newline': true} + ); + }); + + it('Shouldn’t detect eof newline with spaces at the end', function() { + let test = new Test(this); + test.shouldDetect( + ['eof-newline'], + 'a { color: red } ', + {'eof-newline': false} + ); + }); + + it('Should detect eof newline with mixed spaces at the end', function() { + let test = new Test(this); + test.shouldDetect( + ['eof-newline'], + 'a { color: red } \n ', + {'eof-newline': true} + ); + }); + }); +}); + diff --git a/test/options/eof-newline/lint/test.js b/test/options/eof-newline/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/eof-newline/process/test.js b/test/options/eof-newline/process/test.js new file mode 100644 index 00000000..3991f373 --- /dev/null +++ b/test/options/eof-newline/process/test.js @@ -0,0 +1,33 @@ +var assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `eof-newline`, process', function() { + describe('css', function() { + it('Invalid value should not change trim trailing brac', function() { + let test = new Test(this, {'eof-newline': 'foobar'}); + return test.comb.processString( + 'a { color: red } \n' + ).then(function(actual) { + assert.equal(actual, 'a { color: red } \n'); + }); + }); + + it('Boolean true value should insert line-break at eof', function() { + let test = new Test(this, {'eof-newline': true}); + return test.comb.processString( + 'a {color:red} ' + ).then(function(actual) { + assert.equal(actual, 'a {color:red} \n'); + }); + }); + + it('Boolean false value should remove line-break from eof', function() { + let test = new Test(this, {'eof-newline': false}); + return test.comb.processString( + 'a {color:red} \n' + ).then(function(actual) { + assert.equal(actual, 'a {color:red} '); + }); + }); + }); +}); diff --git a/test/options/integral.js b/test/options/integral.js deleted file mode 100644 index 30ecd9f7..00000000 --- a/test/options/integral.js +++ /dev/null @@ -1,22 +0,0 @@ -var assert = require('assert'); - -describe('integral test', function() { - beforeEach(function() { - this.filename = __filename; - }); - - it('Process result must be equal to expected.css', function() { - var config = this.comb.getConfig('csscomb'); - this.comb.configure(config); - this.shouldBeEqual('integral.css', 'integral.expected.css'); - }); - - it('Should detect everything in integral test', function() { - var input = this.readFile('integral.expected.css'); - // Clone the required config object, otherwise other tests would fail - var expected = JSON.parse(JSON.stringify(this.comb.getConfig('csscomb'))); - delete expected['sort-order']; - delete expected['exclude']; - this.shouldDetect(undefined, input, expected); - }); -}); diff --git a/test/options/integral/integral.expected.css b/test/options/integral/detect/css/integral.expected.css similarity index 94% rename from test/options/integral/integral.expected.css rename to test/options/integral/detect/css/integral.expected.css index cf2d68de..f59d0a21 100644 --- a/test/options/integral/integral.expected.css +++ b/test/options/integral/detect/css/integral.expected.css @@ -35,7 +35,9 @@ } /* Фигурные скобки. Вариант 1 */ -a, b, i /* foobar */ +a, +b, +i /* foobar */ { margin: 0; padding: 0; @@ -68,7 +70,8 @@ div p em } @media all /* media */ -{ /* foobar */ +{ + /* foobar */ .input__control { font-size: 100%; @@ -85,7 +88,8 @@ div p em @media screen and (min-width: 35em) /* screen */, print and (min-width: 40em) /* print */ -{ /* foobar */ +{ + /* foobar */ .input__control { -moz-box-sizing: border-box; @@ -123,8 +127,7 @@ div margin: 0; padding: 0; } -/* foo */ -div ~ p +/* foo */ div ~ p { font-size: 1px; @@ -132,7 +135,7 @@ div ~ p } div > p + em { - /* upline comment*/ + /* upline comment*/ font-style: italic; border-bottom: 1px solid red; /* trololo */ /* trololo */ @@ -140,13 +143,9 @@ div > p + em a:not(b) { - top: 0;/* ololo */ - - margin: 0; + top: 0;/* ololo */margin: 0; } b { - top: 0/* trololo */; - - margin: 0; + top: 0/* trololo */;margin: 0; } diff --git a/test/options/integral/detect/test.js b/test/options/integral/detect/test.js new file mode 100644 index 00000000..e4f40a28 --- /dev/null +++ b/test/options/integral/detect/test.js @@ -0,0 +1,15 @@ +let Test = require('../../option_test'); + +describe('Option `integral`, detect', function() { + describe('css', function() { + it('Should detect everything in integral test', function() { + let test = new Test(this); + let input = test.readFile('integral.expected.css'); + // Clone the required config object, otherwise other tests would fail. + let expected = JSON.parse(JSON.stringify(test.Comb.getConfig('csscomb'))); + delete expected['sort-order']; + delete expected.exclude; + test.shouldDetect(undefined, input, expected); + }); + }); +}); diff --git a/test/options/integral/lint/test.js b/test/options/integral/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/integral/integral.css b/test/options/integral/process/css/integral.css similarity index 100% rename from test/options/integral/integral.css rename to test/options/integral/process/css/integral.css diff --git a/test/options/integral/process/css/integral.expected.css b/test/options/integral/process/css/integral.expected.css new file mode 100644 index 00000000..f59d0a21 --- /dev/null +++ b/test/options/integral/process/css/integral.expected.css @@ -0,0 +1,151 @@ +/* foobar */ +@media all and (min-width:0) +{ + .radio-button_theme_normal .radio-button__radio:before + { + background: rgba(0,0,0,.4); + background: -webkit-linear-gradient(top, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + background: -moz-linear-gradient(top, rgba(0,0,0,.2) 0, rgba(0,0,0,.4) 100%); + background: -o-linear-gradient(top, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + background: linear-gradient(to bottom, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + -moz-box-shadow: 0 1px 0 rgba(0,0,0,.07); + box-shadow: 0 1px 0 rgba(0,0,0,.07); + } + + /* :after — фон */ + .radio-button_theme_normal .radio-button__radio[class^='radio']:after + { + content: 'it\'s something different'; + + background: #fff; + background: -webkit-linear-gradient(top, #fff 0,#eee 100%); + background: -moz-linear-gradient(top, #fff 0, #eee 100%); + background: -o-linear-gradient(top, #fff 0,#eee 100%); + background: linear-gradient(to bottom, #fff 0,#eee 100%); + } + + /* _focused_yes */ + .radio-button_theme_normal .radio-button__radio_focused_yes:before + { + content: 'hello'; + + -moz-box-shadow: 0 0 6px 2px rgba(255,204,0,.7), 0 1px 0 rgba(0,0,0,.07); + box-shadow: 0 0 6px 2px rgba(255,204,0,.7), 0 1px 0 rgba(0,0,0,.07); + } +} + +/* Фигурные скобки. Вариант 1 */ +a, +b, +i /* foobar */ +{ + margin: 0; + padding: 0; +} +div p +{ + font-size: 1px; + + top: 0; +} +div p em +{ + font-style: italic; + + border-bottom: 1px solid red; +} + +@media all and (min-width:0) +{ + /* В нажатом состоянии смещается вниз на 1px вся кнопка, текст не смещается */ + .button_pressed_yes.button_shadow_yes + { + top: 1px; + } + + .button_pressed_yes.button_shadow_yes .button__text + { + top: 0; + } +} + +@media all /* media */ +{ + /* foobar */ + .input__control + { + font-size: 100%; + + position: relative; + z-index: 3; + + width: 100%; + margin: 0; + + color: #000; + } +} + +@media screen and (min-width: 35em) /* screen */, + print and (min-width: 40em) /* print */ +{ + /* foobar */ + .input__control + { + -moz-box-sizing: border-box; + box-sizing: border-box; + padding: .4em 0; + + border: 0; + outline: 0; + background: none; + } +} + +/* Фигурные скобки. Вариант 2 */ +div +{ + margin: 0; + padding: 0; +} +div p +{ + font-size: 1px; + + top: 0; +} +div p em +{ + font-style: italic;/* inline comment*/ + + border-bottom: 1px solid red; +} + +/* Фигурные скобки. Вариант 3 */ +div +{ + margin: 0; + padding: 0; +} +/* foo */ div ~ p +{ + font-size: 1px; + + top: 0; +} +div > p + em +{ + /* upline comment*/ + font-style: italic; + + border-bottom: 1px solid red; /* trololo */ /* trololo */ +} + +a:not(b) +{ + top: 0;/* ololo */margin: 0; +} +b +{ + top: 0/* trololo */;margin: 0; +} diff --git a/test/options/integral/process/css/issue-374.css b/test/options/integral/process/css/issue-374.css new file mode 100644 index 00000000..bf667383 --- /dev/null +++ b/test/options/integral/process/css/issue-374.css @@ -0,0 +1,4 @@ +html /deep/ span +{ + display: block; +} diff --git a/test/options/integral/process/sass/issue-252.expected.sass b/test/options/integral/process/sass/issue-252.expected.sass new file mode 100644 index 00000000..f3fb55f5 --- /dev/null +++ b/test/options/integral/process/sass/issue-252.expected.sass @@ -0,0 +1,11 @@ +.partner + font-size: 0 + + color: transparent + + +inline-block + &--motor + background-image: url('/i/partners1.png') + &--motor + background-image: url('/i/partners2.png') + diff --git a/test/options/integral/process/sass/issue-252.sass b/test/options/integral/process/sass/issue-252.sass new file mode 100644 index 00000000..6369eefd --- /dev/null +++ b/test/options/integral/process/sass/issue-252.sass @@ -0,0 +1,8 @@ +.partner + +inline-block + font-size: 0 + color: transparent + &--motor + background-image: url('/i/partners1.png') + &--motor + background-image: url('/i/partners2.png') diff --git a/test/options/integral/process/test.js b/test/options/integral/process/test.js new file mode 100644 index 00000000..6cf8055e --- /dev/null +++ b/test/options/integral/process/test.js @@ -0,0 +1,25 @@ +let Test = require('../../option_test'); + +describe('Option `integral`, process', function() { + describe('css', function() { + it('Process result must be equal to expected.css', function() { + let test = new Test(this); + test.useConfig('csscomb'); + return test.shouldBeEqual('integral.css', 'integral.expected.css'); + }); + + it('Issue 374', function() { + let test = new Test(this); + test.useConfig('csscomb'); + return test.shouldBeEqual('issue-374.css'); + }); + }); + + describe('sass', function() { + it('Issue 252', function() { + let test = new Test(this); + test.useConfig('csscomb'); + return test.shouldBeEqual('issue-252.sass', 'issue-252.expected.sass'); + }); + }); +}); diff --git a/test/options/leading-zero.js b/test/options/leading-zero.js deleted file mode 100644 index dbd5c2b8..00000000 --- a/test/options/leading-zero.js +++ /dev/null @@ -1,51 +0,0 @@ -var assert = require('assert'); - -describe('options/leading-zero', function() { - it('Should add leading zero in dimensions', function() { - this.comb.configure({ 'leading-zero': true }); - assert.equal( - this.comb.processString( - 'div { margin: .5em }' - ), - 'div { margin: 0.5em }' - ); - }); - - it('Should remove leading zero in dimensions', function() { - this.comb.configure({ 'leading-zero': false }); - assert.equal( - this.comb.processString( - 'div { margin: 0.5em }' - ), - 'div { margin: .5em }' - ); - }); - - it('Should detect leading zero option', function() { - this.shouldDetect( - ['leading-zero'], - 'a { width: 0.5em }', - { - 'leading-zero': true - } - ); - }); - - it('Should detect leading zero option set to false', function() { - this.shouldDetect( - ['leading-zero'], - 'a { width: .5em }', - { - 'leading-zero': false - } - ); - }); - - it('Shouldn’t detect leading zero option', function() { - this.shouldDetect( - ['leading-zero'], - 'a { width: 10.5em }', - {} - ); - }); -}); diff --git a/test/options/leading-zero/detect/test.js b/test/options/leading-zero/detect/test.js new file mode 100644 index 00000000..7656ea99 --- /dev/null +++ b/test/options/leading-zero/detect/test.js @@ -0,0 +1,33 @@ +let Test = require('../../option_test'); + +describe('Option `leading-zero`, detect', function() { + describe('css', function() { + it('Should detect leading zero option', function() { + let test = new Test(this); + test.shouldDetect( + ['leading-zero'], + 'a { width: 0.5em }', + {'leading-zero': true} + ); + }); + + it('Should detect leading zero option set to false', function() { + let test = new Test(this); + test.shouldDetect( + ['leading-zero'], + 'a { width: .5em }', + {'leading-zero': false} + ); + }); + + it('Shouldn’t detect leading zero option', function() { + let test = new Test(this); + test.shouldDetect( + ['leading-zero'], + 'a { width: 10.5em }', + {} + ); + }); + }); +}); + diff --git a/test/options/leading-zero/lint/test.js b/test/options/leading-zero/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/leading-zero/process/test.js b/test/options/leading-zero/process/test.js new file mode 100644 index 00000000..d6eef264 --- /dev/null +++ b/test/options/leading-zero/process/test.js @@ -0,0 +1,24 @@ +var assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `leading-zero`, process', function() { + describe('css', function() { + it('Should add leading zero in dimensions', function() { + let test = new Test(this, {'leading-zero': true}); + return test.comb.processString( + 'div { margin: .5em }' + ).then(function(actual) { + assert.equal(actual, 'div { margin: 0.5em }'); + }); + }); + + it('Should remove leading zero in dimensions', function() { + let test = new Test(this, {'leading-zero': false}); + return test.comb.processString( + 'div { margin: 0.5em }' + ).then(function(actual) { + assert.equal(actual, 'div { margin: .5em }'); + }); + }); + }); +}); diff --git a/test/options/lines-between-rulesets/process/css/2-lines-between-rulesets.expected.css b/test/options/lines-between-rulesets/process/css/2-lines-between-rulesets.expected.css new file mode 100644 index 00000000..58bbdff5 --- /dev/null +++ b/test/options/lines-between-rulesets/process/css/2-lines-between-rulesets.expected.css @@ -0,0 +1,7 @@ +.foo {background: red;} + + +/*comment*/.bar{border: 1px solid red;} + + +.baz{color: #fff;} diff --git a/test/options/lines-between-rulesets/process/css/lines-between-rulesets.css b/test/options/lines-between-rulesets/process/css/lines-between-rulesets.css new file mode 100644 index 00000000..30f35c39 --- /dev/null +++ b/test/options/lines-between-rulesets/process/css/lines-between-rulesets.css @@ -0,0 +1 @@ +.foo {background: red;}/*comment*/.bar{border: 1px solid red;}.baz{color: #fff;} diff --git a/test/options/lines-between-rulesets/process/css/lines-between-rulesets.expected.css b/test/options/lines-between-rulesets/process/css/lines-between-rulesets.expected.css new file mode 100644 index 00000000..ad0651d5 --- /dev/null +++ b/test/options/lines-between-rulesets/process/css/lines-between-rulesets.expected.css @@ -0,0 +1,5 @@ +.foo {background: red;} + +/*comment*/.bar{border: 1px solid red;} + +.baz{color: #fff;} diff --git a/test/options/lines-between-rulesets/process/less/2-lines-between-rulesets.expected.less b/test/options/lines-between-rulesets/process/less/2-lines-between-rulesets.expected.less new file mode 100644 index 00000000..7e8b5e9e --- /dev/null +++ b/test/options/lines-between-rulesets/process/less/2-lines-between-rulesets.expected.less @@ -0,0 +1,40 @@ +.foo { + .border-radius(5px); + background: red; + + + /* comment */ + .omg { + .test { + height: 50px; + } + } +} + + +.bar { + border: 1px solid red; + + + /* + ** another comment + ** spanning multiple lines + */ + @media (min-width: 500px) { + width: 50px; + } +} + + +.baz { + @grey: #ccc; + + + // a single-line comment + // another single-line comment + .wtf { + @media only screen { + background-color: @grey; + } + } +} diff --git a/test/options/lines-between-rulesets/process/less/lines-between-rulesets.expected.less b/test/options/lines-between-rulesets/process/less/lines-between-rulesets.expected.less new file mode 100644 index 00000000..ef3a7ef3 --- /dev/null +++ b/test/options/lines-between-rulesets/process/less/lines-between-rulesets.expected.less @@ -0,0 +1,35 @@ +.foo { + .border-radius(5px); + background: red; + + /* comment */ + .omg { + .test { + height: 50px; + } + } +} + +.bar { + border: 1px solid red; + + /* + ** another comment + ** spanning multiple lines + */ + @media (min-width: 500px) { + width: 50px; + } +} + +.baz { + @grey: #ccc; + + // a single-line comment + // another single-line comment + .wtf { + @media only screen { + background-color: @grey; + } + } +} diff --git a/test/options/lines-between-rulesets/process/less/lines-between-rulesets.less b/test/options/lines-between-rulesets/process/less/lines-between-rulesets.less new file mode 100644 index 00000000..544895b5 --- /dev/null +++ b/test/options/lines-between-rulesets/process/less/lines-between-rulesets.less @@ -0,0 +1,28 @@ +.foo { + .border-radius(5px); + background: red; + /* comment */ + .omg { + .test { + height: 50px; + } + } +}.bar { + border: 1px solid red; + /* + ** another comment + ** spanning multiple lines + */ + @media (min-width: 500px) { + width: 50px; + } +}.baz { + @grey: #ccc; + // a single-line comment + // another single-line comment + .wtf { + @media only screen { + background-color: @grey; + } + } +} diff --git a/test/options/lines-between-rulesets/process/sass/2-lines-between-rulesets.expected.sass b/test/options/lines-between-rulesets/process/sass/2-lines-between-rulesets.expected.sass new file mode 100644 index 00000000..b35b9173 --- /dev/null +++ b/test/options/lines-between-rulesets/process/sass/2-lines-between-rulesets.expected.sass @@ -0,0 +1,32 @@ +.foo + +border-radius(5px) + background: red + + + // comment + .omg + .test + height: 50px + + +.bar + border: 1px solid red + + + /* + * another comment + * spanning multiple lines + */ + @media (min-width: 500px) + width: 50px + + +.baz + $grey: #ccc + + + // a single-line comment + // another single-line comment + .wtf + @media only screen + background-color: $grey diff --git a/test/options/lines-between-rulesets/process/sass/lines-between-rulesets.expected.sass b/test/options/lines-between-rulesets/process/sass/lines-between-rulesets.expected.sass new file mode 100644 index 00000000..31a2a3d8 --- /dev/null +++ b/test/options/lines-between-rulesets/process/sass/lines-between-rulesets.expected.sass @@ -0,0 +1,27 @@ +.foo + +border-radius(5px) + background: red + + // comment + .omg + .test + height: 50px + +.bar + border: 1px solid red + + /* + * another comment + * spanning multiple lines + */ + @media (min-width: 500px) + width: 50px + +.baz + $grey: #ccc + + // a single-line comment + // another single-line comment + .wtf + @media only screen + background-color: $grey diff --git a/test/options/lines-between-rulesets/process/sass/lines-between-rulesets.sass b/test/options/lines-between-rulesets/process/sass/lines-between-rulesets.sass new file mode 100644 index 00000000..10ee7156 --- /dev/null +++ b/test/options/lines-between-rulesets/process/sass/lines-between-rulesets.sass @@ -0,0 +1,22 @@ +.foo + +border-radius(5px) + background: red + // comment + .omg + .test + height: 50px +.bar + border: 1px solid red + /* + * another comment + * spanning multiple lines + */ + @media (min-width: 500px) + width: 50px +.baz + $grey: #ccc + // a single-line comment + // another single-line comment + .wtf + @media only screen + background-color: $grey diff --git a/test/options/lines-between-rulesets/process/scss/2-lines-between-rulesets.expected.scss b/test/options/lines-between-rulesets/process/scss/2-lines-between-rulesets.expected.scss new file mode 100644 index 00000000..386fbb18 --- /dev/null +++ b/test/options/lines-between-rulesets/process/scss/2-lines-between-rulesets.expected.scss @@ -0,0 +1,40 @@ +.foo { + @include border-radius(5px); + background: red; + + + /* comment */ + .omg { + .test { + height: 50px; + } + } +} + + +.bar { + border: 1px solid red; + + + /* + ** another comment + ** spanning multiple lines + */ + @media (min-width: 500px) { + width: 50px; + } +} + + +.baz { + $grey: #ccc; + + + // a single-line comment + // another single-line comment + .wtf { + @media only screen { + background-color: $grey; + } + } +} diff --git a/test/options/lines-between-rulesets/process/scss/lines-between-rulesets.expected.scss b/test/options/lines-between-rulesets/process/scss/lines-between-rulesets.expected.scss new file mode 100644 index 00000000..db92922e --- /dev/null +++ b/test/options/lines-between-rulesets/process/scss/lines-between-rulesets.expected.scss @@ -0,0 +1,35 @@ +.foo { + @include border-radius(5px); + background: red; + + /* comment */ + .omg { + .test { + height: 50px; + } + } +} + +.bar { + border: 1px solid red; + + /* + ** another comment + ** spanning multiple lines + */ + @media (min-width: 500px) { + width: 50px; + } +} + +.baz { + $grey: #ccc; + + // a single-line comment + // another single-line comment + .wtf { + @media only screen { + background-color: $grey; + } + } +} diff --git a/test/options/lines-between-rulesets/process/scss/lines-between-rulesets.scss b/test/options/lines-between-rulesets/process/scss/lines-between-rulesets.scss new file mode 100644 index 00000000..efa5b90f --- /dev/null +++ b/test/options/lines-between-rulesets/process/scss/lines-between-rulesets.scss @@ -0,0 +1,28 @@ +.foo { + @include border-radius(5px); + background: red; + /* comment */ + .omg { + .test { + height: 50px; + } + } +}.bar { + border: 1px solid red; + /* + ** another comment + ** spanning multiple lines + */ + @media (min-width: 500px) { + width: 50px; + } +}.baz { + $grey: #ccc; + // a single-line comment + // another single-line comment + .wtf { + @media only screen { + background-color: $grey; + } + } +} diff --git a/test/options/lines-between-rulesets/process/test.js b/test/options/lines-between-rulesets/process/test.js new file mode 100644 index 00000000..b6f935ca --- /dev/null +++ b/test/options/lines-between-rulesets/process/test.js @@ -0,0 +1,93 @@ +'use strict'; + +let Test = require('../../option_test'); + +describe('Option `lines-between-rulesets`, process', function() { + describe('css', function() { + it('Numeric value => should insert 1 newline between rulesets', function() { + let test = new Test(this, { 'lines-between-rulesets': 1 }); + return test.shouldBeEqual('lines-between-rulesets.css', 'lines-between-rulesets.expected.css'); + }); + + it('Numeric value => should insert multiple newlines between rulesets', function() { + let test = new Test(this, { 'lines-between-rulesets': 2 }); + return test.shouldBeEqual('lines-between-rulesets.css', '2-lines-between-rulesets.expected.css'); + }); + + it('Invalid string value => should should not change anything', function() { + let test = new Test(this, { 'lines-between-rulesets': '\n' }); + return test.shouldBeEqual('lines-between-rulesets.css'); + }); + + it('Float number value => should be rounded', function() { + let test = new Test(this, { 'lines-between-rulesets': 0.5 }); + return test.shouldBeEqual('lines-between-rulesets.css', 'lines-between-rulesets.expected.css'); + }); + }); + + describe('less', function() { + it('Numeric value => should insert 1 newline between rulesets', function() { + let test = new Test(this, { 'lines-between-rulesets': 1 }); + return test.shouldBeEqual('lines-between-rulesets.less', 'lines-between-rulesets.expected.less'); + }); + + it('Numeric value => should insert multiple newlines between rulesets', function() { + let test = new Test(this, { 'lines-between-rulesets': 2 }); + return test.shouldBeEqual('lines-between-rulesets.less', '2-lines-between-rulesets.expected.less'); + }); + + it('Invalid string value => should should not change anything', function() { + let test = new Test(this, { 'lines-between-rulesets': '\n' }); + return test.shouldBeEqual('lines-between-rulesets.less'); + }); + + it('Float number value => should be rounded', function() { + let test = new Test(this, { 'lines-between-rulesets': 0.5 }); + return test.shouldBeEqual('lines-between-rulesets.less', 'lines-between-rulesets.expected.less'); + }); + }); + + describe('scss', function() { + it('Numeric value => should insert 1 newline between rulesets', function() { + let test = new Test(this, { 'lines-between-rulesets': 1 }); + return test.shouldBeEqual('lines-between-rulesets.scss', 'lines-between-rulesets.expected.scss'); + }); + + it('Numeric value => should insert multiple newlines between rulesets', function() { + let test = new Test(this, { 'lines-between-rulesets': 2 }); + return test.shouldBeEqual('lines-between-rulesets.scss', '2-lines-between-rulesets.expected.scss'); + }); + + it('Invalid string value => should should not change anything', function() { + let test = new Test(this, { 'lines-between-rulesets': '\n' }); + return test.shouldBeEqual('lines-between-rulesets.scss'); + }); + + it('Float number value => should be rounded', function() { + let test = new Test(this, { 'lines-between-rulesets': 0.5 }); + return test.shouldBeEqual('lines-between-rulesets.scss', 'lines-between-rulesets.expected.scss'); + }); + }); + + describe('sass', function() { + it('Numeric value => should insert 1 newline between rulesets', function() { + let test = new Test(this, { 'lines-between-rulesets': 1 }); + return test.shouldBeEqual('lines-between-rulesets.sass', 'lines-between-rulesets.expected.sass'); + }); + + it('Numeric value => should insert multiple newlines between rulesets', function() { + let test = new Test(this, { 'lines-between-rulesets': 2 }); + return test.shouldBeEqual('lines-between-rulesets.sass', '2-lines-between-rulesets.expected.sass'); + }); + + it('Invalid string value => should should not change anything', function() { + let test = new Test(this, { 'lines-between-rulesets': '\n' }); + return test.shouldBeEqual('lines-between-rulesets.sass'); + }); + + it('Float number value => should be rounded', function() { + let test = new Test(this, { 'lines-between-rulesets': 0.5 }); + return test.shouldBeEqual('lines-between-rulesets.sass', 'lines-between-rulesets.expected.sass'); + }); + }); +}); diff --git a/test/options/option_test.js b/test/options/option_test.js new file mode 100644 index 00000000..e17e7e87 --- /dev/null +++ b/test/options/option_test.js @@ -0,0 +1,54 @@ +let assert = require('assert'); +let fs = require('fs'); +let path = require('path'); + +let Comb = require('../../lib/csscomb'); + +class OptionTest { + constructor(context, config) { + this.file = context.test.file; + this.syntax = context.test.parent.title; + + this.Comb = Comb; + this.comb = new Comb(); + if (config) this.comb.configure(config); + } + + useConfig(name) { + let config = Comb.getConfig(name); + this.comb.configure(config); + } + + getErrors(filename) { + let input = this.readFile(filename); + return this.comb.lintString(input, {syntax: this.syntax}); + } + + shouldBeEqual(inputFile, expectedFile) { + let input = this.readFile(inputFile); + let expected = expectedFile ? this.readFile(expectedFile) : input; + + return this.comb.processString(input, {syntax: this.syntax}) + .then(string => assert.equal(string, expected)); + } + + /** + * Detect options in a file and compare result with expected. + * File names should be relative to test suite's folder. + * @param {Array} options List of options that should be detected + * @param {String} input Name of template file + * @param {Object} expected Expected config with detected options + */ + shouldDetect(options, input, expected) { + let detectedConfig = Comb.detectInString(input, options); + assert.deepEqual(detectedConfig, expected); + } + + readFile(filename) { + let dirname = path.dirname(this.file); + let filePath = path.join(dirname, this.syntax, filename); + return fs.readFileSync(filePath, 'utf8'); + } +} + +module.exports = OptionTest; diff --git a/test/options/quotes.js b/test/options/quotes.js deleted file mode 100644 index 5ed3a898..00000000 --- a/test/options/quotes.js +++ /dev/null @@ -1,120 +0,0 @@ -var assert = require('assert'); - -describe('options/quotes', function() { - it('Invalid String should not change quotes', function() { - this.comb.configure({ quotes: 3 }); - assert.equal( - this.comb.processString( - 'a { content: "" }' + - 'b { content: \'\' }' - ), - 'a { content: "" }' + - 'b { content: \'\' }' - ); - }); - - it('`single` value should set the quotes to single', function() { - this.comb.configure({ quotes: 'single' }); - assert.equal( - this.comb.processString( - 'a { content: "" }' + - 'b { content: \'\' }' - ), - 'a { content: \'\' }' + - 'b { content: \'\' }' - ); - }); - - it('`double` value should set the quotes to double', function() { - this.comb.configure({ quotes: 'double' }); - assert.equal( - this.comb.processString( - 'a { content: "" }' + - 'b { content: \'\' }' - ), - 'a { content: "" }' + - 'b { content: "" }' - ); - }); - - it('`double` value should set the quotes to double in attrs and urls', function() { - this.comb.configure({ quotes: 'double' }); - assert.equal( - this.comb.processString( - 'a[class^=\'foo\'] { background: url(\'foo.png\') }' - ), - 'a[class^="foo"] { background: url("foo.png") }' - ); - }); - - it('`double` value should escape the unescaped double quotes on change', function() { - this.comb.configure({ quotes: 'double' }); - assert.equal( - this.comb.processString( - 'a { content: "\\"" }' + - 'b { content: \'"\' }' - ), - 'a { content: "\\"" }' + - 'b { content: "\\"" }' - ); - }); - - - it('`single` value should unescape the escaped double quotes on change', function() { - this.comb.configure({ quotes: 'single' }); - assert.equal( - this.comb.processString( - 'a { content: "\\"" }' - ), - 'a { content: \'"\' }' - ); - }); - - it('Should not detect quotes when there are none', function() { - this.shouldDetect( - ['quotes'], - 'a { color:red }', - {} - ); - }); - - it('Should detect double quotes', function() { - this.shouldDetect( - ['quotes'], - 'a { content: "foo" }', - { - quotes: 'double' - } - ); - }); - - it('Should detect single quotes', function() { - this.shouldDetect( - ['quotes'], - 'a { content: \'foo\' }', - { - quotes: 'single' - } - ); - }); - - it('Should detect single quotes in attribute', function() { - this.shouldDetect( - ['quotes'], - 'a[class^=\'foo\'] { color: red }', - { - quotes: 'single' - } - ); - }); - - it('Should detect double quotes in url', function() { - this.shouldDetect( - ['quotes'], - 'a { background: url("foo.png") }', - { - quotes: 'double' - } - ); - }); -}); diff --git a/test/options/quotes/detect/test.js b/test/options/quotes/detect/test.js new file mode 100644 index 00000000..959a4abb --- /dev/null +++ b/test/options/quotes/detect/test.js @@ -0,0 +1,51 @@ +let Test = require('../../option_test'); + +describe('Option `quotes`, detect', function() { + describe('css', function() { + it('Should not detect quotes when there are none', function() { + let test = new Test(this); + test.shouldDetect( + ['quotes'], + 'a { color:red }', + {} + ); + }); + + it('Should detect double quotes', function() { + let test = new Test(this); + test.shouldDetect( + ['quotes'], + 'a { content: "foo" }', + {quotes: 'double'} + ); + }); + + it('Should detect single quotes', function() { + let test = new Test(this); + test.shouldDetect( + ['quotes'], + 'a { content: \'foo\' }', + {quotes: 'single'} + ); + }); + + it('Should detect single quotes in attribute', function() { + let test = new Test(this); + test.shouldDetect( + ['quotes'], + 'a[class^=\'foo\'] { color: red }', + {quotes: 'single'} + ); + }); + + it('Should detect double quotes in url', function() { + let test = new Test(this); + test.shouldDetect( + ['quotes'], + 'a { background: url("foo.png") }', + {quotes: 'double'} + ); + }); + }); +}); + diff --git a/test/options/quotes/lint/test.js b/test/options/quotes/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/quotes/process/test.js b/test/options/quotes/process/test.js new file mode 100644 index 00000000..e37562e2 --- /dev/null +++ b/test/options/quotes/process/test.js @@ -0,0 +1,60 @@ +var assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `quotes`, process', function() { + describe('css', function() { + it('Invalid String should not change quotes', function() { + let test = new Test(this, {quotes: 3}); + return test.comb.processString( + 'a { content: "" }b { content: \'\' }' + ).then(function(actual) { + assert.equal(actual, 'a { content: "" }b { content: \'\' }'); + }); + }); + + it('`single` value should set the quotes to single', function() { + let test = new Test(this, {quotes: 'single'}); + return test.comb.processString( + 'a { content: "" }b { content: \'\' }' + ).then(function(actual) { + assert.equal(actual, 'a { content: \'\' }b { content: \'\' }'); + }); + }); + + it('`double` value should set the quotes to double', function() { + let test = new Test(this, {quotes: 'double'}); + return test.comb.processString( + 'a { content: "" }b { content: \'\' }' + ).then(function(actual) { + assert.equal(actual, 'a { content: "" }b { content: "" }'); + }); + }); + + it('`double` value should set the quotes to double in attrs and urls', function() { + let test = new Test(this, {quotes: 'double'}); + return test.comb.processString( + 'a[class^=\'foo\'] { background: url(\'foo.png\') }' + ).then(function(actual) { + assert.equal(actual, 'a[class^="foo"] { background: url("foo.png") }'); + }); + }); + + it('`double` value should escape the unescaped double quotes on change', function() { + let test = new Test(this, {quotes: 'double'}); + return test.comb.processString( + 'a { content: "\\"" }b { content: \'"\' }' + ).then(function(actual) { + assert.equal(actual, 'a { content: "\\"" }b { content: "\\"" }'); + }); + }); + + it('`single` value should unescape the escaped double quotes on change', function() { + let test = new Test(this, {quotes: 'single'}); + return test.comb.processString( + 'a { content: "\\"" }' + ).then(function(actual) { + assert.equal(actual, 'a { content: \'"\' }'); + }); + }); + }); +}); diff --git a/test/options/remove-empty-rulesets-scss.js b/test/options/remove-empty-rulesets-scss.js deleted file mode 100644 index 237b0e3f..00000000 --- a/test/options/remove-empty-rulesets-scss.js +++ /dev/null @@ -1,22 +0,0 @@ -describe('options/remove-empty-rulesets (scss)', function() { - beforeEach(function() { - this.filename = __filename; - this.comb.configure({ 'remove-empty-rulesets': true }); - }); - - it('Should not remove rulesets which contain only includes', function() { - this.shouldBeEqual('include.scss'); - }); - - it('Should remove rulesets with contain only empty nested rules', function() { - this.shouldBeEqual('empty-nested-rule.scss', 'empty-nested-rule.expected.scss'); - }); - - it('Should not remove rulesets with non-empty nested rules. Test 1', function() { - this.shouldBeEqual('nested-rule-1.scss'); - }); - - it('Should not remove rulesets with non-empty nested rules. Test 2', function() { - this.shouldBeEqual('nested-rule-2.scss', 'nested-rule-2.expected.scss'); - }); -}); diff --git a/test/options/remove-empty-rulesets.js b/test/options/remove-empty-rulesets.js deleted file mode 100644 index 0c045c50..00000000 --- a/test/options/remove-empty-rulesets.js +++ /dev/null @@ -1,169 +0,0 @@ -var assert = require('assert'); - -describe('options/remove-empty-rulesets', function() { - it('Configured with invalid value, should not remove empty ruleset', function() { - this.comb.configure({ 'remove-empty-rulesets': 'foobar' }); - assert.equal(this.comb.processString('a { width: 10px; } b {}'), 'a { width: 10px; } b {}'); - }); - - describe('configured with Boolean "true" value', function() { - beforeEach(function() { - this.comb.configure({ 'remove-empty-rulesets': true }); - }); - - it('should remove empty ruleset', function() { - assert.equal(this.comb.processString(' b {} '), ' '); - }); - - it('should remove ruleset with spaces', function() { - assert.equal(this.comb.processString(' b { } '), ' '); - }); - - it('should leave ruleset with declarations', function() { - assert.equal(this.comb.processString('a { width: 10px; }\nb {} '), 'a { width: 10px; }\n '); - }); - - it('should leave ruleset with comments', function() { - assert.equal(this.comb.processString('a { /* comment */ }\nb {} '), 'a { /* comment */ }\n '); - }); - }); - - describe('detecting the value', function() { - it('Should detect this option set to `true`', function() { - this.shouldDetect( - ['remove-empty-rulesets'], - 'a { color: red }', - { - 'remove-empty-rulesets': true - } - ); - }); - - it('Should detect this option set to `false` with empty block', function() { - this.shouldDetect( - ['remove-empty-rulesets'], - 'a {}', - { - 'remove-empty-rulesets': false - } - ); - }); - - it('Should detect this option set to `false` with block containing whitespace', function() { - this.shouldDetect( - ['remove-empty-rulesets'], - 'a { }', - { - 'remove-empty-rulesets': false - } - ); - }); - - it('Should detect this option set to `true` with block containing comment', function() { - this.shouldDetect( - ['remove-empty-rulesets'], - 'a { /* Hello */ }', - { - 'remove-empty-rulesets': true - } - ); - }); - - it('Should detect this option set to `true` with media query containing block', function() { - this.shouldDetect( - ['remove-empty-rulesets'], - '@media all and (min-width:0) { a { /* Hello */ } }', - { - 'remove-empty-rulesets': true - } - ); - }); - - it('Should detect this option set to `true` with media query containing comment', function() { - this.shouldDetect( - ['remove-empty-rulesets'], - '@media all and (min-width:0) {/* Hello */}', - { - 'remove-empty-rulesets': true - } - ); - }); - - it('Should detect this option set to `false` with empty media query', function() { - this.shouldDetect( - ['remove-empty-rulesets'], - '@media all and (min-width:0) {}', - { - 'remove-empty-rulesets': false - } - ); - }); - - it('Should detect this option set to `false` with media query containing whitespace', function() { - this.shouldDetect( - ['remove-empty-rulesets'], - '@media all and (min-width:0) { \n }', - { - 'remove-empty-rulesets': false - } - ); - }); - }); -}); - -describe('options/remove-empty-rulesets AST manipulation', function() { - var rule = require('../../lib/options/remove-empty-rulesets.js'); - var nodeContent; - - describe('merge adjacent whitespace', function() { - it('should do nothing with empty content', function() { - nodeContent = []; - rule._mergeAdjacentWhitespace(nodeContent); - assert.deepEqual(nodeContent, []); - }); - - it('should do nothing with only one whitespace', function() { - nodeContent = [['s', ' ']]; - rule._mergeAdjacentWhitespace(nodeContent); - assert.deepEqual(nodeContent, [['s', ' ']]); - }); - - it('should merge two adjacent whitespaces', function() { - nodeContent = [['s', ' '], ['s', ' \n']]; - rule._mergeAdjacentWhitespace(nodeContent); - assert.deepEqual(nodeContent, [['s', ' \n']]); - }); - - it('should merge three adjacent whitespaces', function() { - nodeContent = [['s', ' '], ['s', ' \n'], ['s', ' \n']]; - rule._mergeAdjacentWhitespace(nodeContent); - assert.deepEqual(nodeContent, [['s', ' \n \n']]); - }); - }); - - describe('remove empty rulesets', function() { - it('should do nothing with empty content', function() { - nodeContent = []; - rule._removeEmptyRulesets(nodeContent); - assert.deepEqual(nodeContent, []); - }); - - it('should do nothing with no rulesets', function() { - nodeContent = [['s', ' ']]; - rule._removeEmptyRulesets(nodeContent); - assert.deepEqual(nodeContent, [['s', ' ']]); - }); - - it('should remove empty ruleset', function() { - nodeContent = [['ruleset', []]]; - rule._removeEmptyRulesets(nodeContent); - assert.deepEqual(nodeContent, []); - }); - - it('should remove two empty rulesets', function() { - nodeContent = [['s', ' '], ['ruleset', []], ['s', ' \n'], ['ruleset', []]]; - rule._removeEmptyRulesets(nodeContent); - assert.deepEqual(nodeContent, [['s', ' '], ['s', ' \n']]); - }); - }); -}); diff --git a/test/options/remove-empty-rulesets/detect/test.js b/test/options/remove-empty-rulesets/detect/test.js new file mode 100644 index 00000000..2fa734f5 --- /dev/null +++ b/test/options/remove-empty-rulesets/detect/test.js @@ -0,0 +1,78 @@ +let Test = require('../../option_test'); + +describe('Option `remove-empty-rulesets`, detect', function() { + describe('css', function() { + it('Should detect test option set to `true`', function() { + let test = new Test(this); + test.shouldDetect( + ['remove-empty-rulesets'], + 'a { color: red }', + {'remove-empty-rulesets': true} + ); + }); + + it('Should detect test option set to `false` with empty block', function() { + let test = new Test(this); + test.shouldDetect( + ['remove-empty-rulesets'], + 'a {}', + {'remove-empty-rulesets': false} + ); + }); + + it('Should detect test option set to `false` with block containing whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['remove-empty-rulesets'], + 'a { }', + {'remove-empty-rulesets': false} + ); + }); + + it('Should detect test option set to `true` with block containing comment', function() { + let test = new Test(this); + test.shouldDetect( + ['remove-empty-rulesets'], + 'a { /* Hello */ }', + {'remove-empty-rulesets': true} + ); + }); + + it('Should detect test option set to `true` with media query containing block', function() { + let test = new Test(this); + test.shouldDetect( + ['remove-empty-rulesets'], + '@media all and (min-width:0) { a { /* Hello */ } }', + {'remove-empty-rulesets': true} + ); + }); + + it('Should detect test option set to `true` with media query containing comment', function() { + let test = new Test(this); + test.shouldDetect( + ['remove-empty-rulesets'], + '@media all and (min-width:0) {/* Hello */}', + {'remove-empty-rulesets': true} + ); + }); + + it('Should detect test option set to `false` with empty media query', function() { + let test = new Test(this); + test.shouldDetect( + ['remove-empty-rulesets'], + '@media all and (min-width:0) {}', + {'remove-empty-rulesets': false} + ); + }); + + it('Should detect test option set to `false` with media query containing whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['remove-empty-rulesets'], + '@media all and (min-width:0) { \n }', + {'remove-empty-rulesets': false} + ); + }); + }); +}); + diff --git a/test/options/remove-empty-rulesets/lint/test.js b/test/options/remove-empty-rulesets/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/remove-empty-rulesets/process/less/1.expected.less b/test/options/remove-empty-rulesets/process/less/1.expected.less new file mode 100644 index 00000000..fbb76512 --- /dev/null +++ b/test/options/remove-empty-rulesets/process/less/1.expected.less @@ -0,0 +1,14 @@ +#a { + + #b { + text-align: right; + } + + + + #d { + .e { + text-align: center; + } + } +} diff --git a/test/options/remove-empty-rulesets/process/less/1.less b/test/options/remove-empty-rulesets/process/less/1.less new file mode 100644 index 00000000..6abc2477 --- /dev/null +++ b/test/options/remove-empty-rulesets/process/less/1.less @@ -0,0 +1,14 @@ +#a { + + #b { + text-align: right; + } + + #c { } + + #d { + .e { + text-align: center; + } + } +} diff --git a/test/options/remove-empty-rulesets-scss/empty-nested-rule.expected.scss b/test/options/remove-empty-rulesets/process/scss/empty-nested-rule.expected.scss similarity index 100% rename from test/options/remove-empty-rulesets-scss/empty-nested-rule.expected.scss rename to test/options/remove-empty-rulesets/process/scss/empty-nested-rule.expected.scss diff --git a/test/options/remove-empty-rulesets-scss/empty-nested-rule.scss b/test/options/remove-empty-rulesets/process/scss/empty-nested-rule.scss similarity index 100% rename from test/options/remove-empty-rulesets-scss/empty-nested-rule.scss rename to test/options/remove-empty-rulesets/process/scss/empty-nested-rule.scss diff --git a/test/options/remove-empty-rulesets-scss/include.scss b/test/options/remove-empty-rulesets/process/scss/include.scss similarity index 100% rename from test/options/remove-empty-rulesets-scss/include.scss rename to test/options/remove-empty-rulesets/process/scss/include.scss diff --git a/test/options/remove-empty-rulesets-scss/nested-rule-1.scss b/test/options/remove-empty-rulesets/process/scss/nested-rule-1.scss similarity index 100% rename from test/options/remove-empty-rulesets-scss/nested-rule-1.scss rename to test/options/remove-empty-rulesets/process/scss/nested-rule-1.scss diff --git a/test/options/remove-empty-rulesets-scss/nested-rule-2.expected.scss b/test/options/remove-empty-rulesets/process/scss/nested-rule-2.expected.scss similarity index 100% rename from test/options/remove-empty-rulesets-scss/nested-rule-2.expected.scss rename to test/options/remove-empty-rulesets/process/scss/nested-rule-2.expected.scss diff --git a/test/options/remove-empty-rulesets-scss/nested-rule-2.scss b/test/options/remove-empty-rulesets/process/scss/nested-rule-2.scss similarity index 100% rename from test/options/remove-empty-rulesets-scss/nested-rule-2.scss rename to test/options/remove-empty-rulesets/process/scss/nested-rule-2.scss diff --git a/test/options/remove-empty-rulesets/process/test.js b/test/options/remove-empty-rulesets/process/test.js new file mode 100644 index 00000000..c80097b5 --- /dev/null +++ b/test/options/remove-empty-rulesets/process/test.js @@ -0,0 +1,96 @@ +var assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `remove-empty-rulesets`, process', function() { + describe('css', function() { + it('Configured with invalid value, should not remove empty ruleset', function() { + let test = new Test(this, {'remove-empty-rulesets': 'foobar'}); + return test.comb.processString('a { width: 10px; } b {}') + .then(function(actual) { + assert.equal(actual, 'a { width: 10px; } b {}'); + }); + }); + + it('Should remove empty ruleset', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + return test.comb.processString(' b {} ') + .then(function(actual) { + assert.equal(actual, ' '); + }); + }); + + it('Should remove ruleset with spaces', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + return test.comb.processString(' b { } ') + .then(function(actual) { + assert.equal(actual, ' '); + }); + }); + + it('Should leave ruleset with declarations', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + return test.comb.processString('a { width: 10px; }\nb {} ') + .then(function(actual) { + assert.equal(actual, 'a { width: 10px; }\n '); + }); + }); + + it('Should leave ruleset with comments', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + return test.comb.processString('a { /* comment */ }\nb {} ') + .then(function(actual) { + assert.equal(actual, 'a { /* comment */ }\n '); + }); + }); + }); + + describe('less', function() { + it('Issue 201. Test 1', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + return test.shouldBeEqual('1.less', '1.expected.less'); + }); + + it('Issue 201. Test 2', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + let string = '#a {#b {} #d {}}'; + return test.comb.processString(string, {syntax: 'less'}) + .then(function(actual) { + assert.equal(actual, ''); + }); + }); + + it('Issue 201. Test 3', function() { + let test = new Test(this, { + 'remove-empty-rulesets': false, + 'always-semicolon': true + }); + let string = '#a {#b {} #d {}}'; + return test.comb.processString(string, {syntax: 'less'}) + .then(function(actual) { + assert.equal(actual, string); + }); + }); + }); + + describe('scss', function() { + it('Should not remove rulesets which contain only includes', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + return test.shouldBeEqual('include.scss'); + }); + + it('Should remove rulesets with contain only empty nested rules', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + return test.shouldBeEqual('empty-nested-rule.scss', 'empty-nested-rule.expected.scss'); + }); + + it('Should not remove rulesets with non-empty nested rules. Test 1', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + return test.shouldBeEqual('nested-rule-1.scss'); + }); + + it('Should not remove rulesets with non-empty nested rules. Test 2', function() { + let test = new Test(this, {'remove-empty-rulesets': true}); + return test.shouldBeEqual('nested-rule-2.scss', 'nested-rule-2.expected.scss'); + }); + }); +}); diff --git a/test/options/rule-indent.js b/test/options/rule-indent.js deleted file mode 100644 index 1b42e9e6..00000000 --- a/test/options/rule-indent.js +++ /dev/null @@ -1,97 +0,0 @@ -var assert = require('assert'); - -describe('options/rule-indent', function() { - it('Invalid Number value should not change rule indent', function() { - var input = 'a {\n color: red }'; - this.comb.configure({ 'rule-indent': 3.5 }); - assert.equal(this.comb.processString(input), input); - }); - - it('Invalid String value should not change rule indent', function() { - var input = 'a {\n color: red }'; - this.comb.configure({ 'rule-indent': 'foobar' }); - assert.equal(this.comb.processString(input), input); - }); - - it('Valid Number value should set equal space indent', function() { - this.comb.configure({ 'rule-indent': 3 }); - assert.equal( - this.comb.processString('a {\n color: red }'), - 'a {\n color: red }' - ); - }); - - it('Valid String value should set equal indent', function() { - this.comb.configure({ 'rule-indent': '\t' }); - assert.equal( - this.comb.processString( - 'a{color:red;background:#fff}\n' + - 'a { color: red; background: #fff; }\n' + - 'a {\ncolor:red;\n\nbackground: #fff}\n' + - 'a { /* foo */ color:red; /* bar */\n\nbackground: #fff\n}\n' - ), - 'a{\n\tcolor:red;\n\tbackground:#fff}\n' + - 'a {\n\tcolor: red;\n\tbackground: #fff; }\n' + - 'a {\n\tcolor:red;\n\n\tbackground: #fff}\n' + - 'a { /* foo */\n\tcolor:red; /* bar */\n\n\tbackground: #fff\n}\n' - ); - }); - - it('Valid value should ignore empty blocks', function() { - this.comb.configure({ 'rule-indent': ' ' }); - assert.equal( - this.comb.processString('a {}'), - 'a {}' - ); - }); - - it('Should detect the empty rule-indent option', function() { - this.shouldDetect( - ['rule-indent'], - 'a{\ncolor: red\n}', - { - 'rule-indent': '' - } - ); - }); - - it('Should detect the rule-indent option equal to four spaces', function() { - this.shouldDetect( - ['rule-indent'], - 'a{\n color: red\n}', - { - 'rule-indent': ' ' - } - ); - }); - - it('Should detect the rule-indent option equal to a tab', function() { - this.shouldDetect( - ['rule-indent'], - 'a{\n\tcolor: red\n}', - { - 'rule-indent': '\t' - } - ); - }); - - it('Should detect the rule-indent option equal to two spaces inside a mq', function() { - this.shouldDetect( - ['rule-indent'], - '@media all {\n .input__control {\n color: #000;\n }\n}', - { - 'rule-indent': ' ' - } - ); - }); - - it('Should detect the rule-indent option equal to a tab with a lot of whitespaces', function() { - this.shouldDetect( - ['rule-indent'], - 'a{\n\t\n\tcolor: red\n}', - { - 'rule-indent': '\t' - } - ); - }); -}); diff --git a/test/options/sort-order-fallback/detect/test.js b/test/options/sort-order-fallback/detect/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/sort-order-fallback/lint/test.js b/test/options/sort-order-fallback/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/sort-order-fallback/process/css/same-property.css b/test/options/sort-order-fallback/process/css/same-property.css new file mode 100644 index 00000000..8af2a139 --- /dev/null +++ b/test/options/sort-order-fallback/process/css/same-property.css @@ -0,0 +1,4 @@ +button { + outline: 1px dotted; + outline: 5px auto -webkit-focus-ring-color; +} diff --git a/test/options/sort-order-fallback/process/css/test-2.expected.css b/test/options/sort-order-fallback/process/css/test-2.expected.css new file mode 100644 index 00000000..dd35868e --- /dev/null +++ b/test/options/sort-order-fallback/process/css/test-2.expected.css @@ -0,0 +1,11 @@ +a { + top: 0; + left: 0; + + -moz-color: zebra; + color: tomato; + -webkit-float: left; + float: right; + font-size: 1em; + position: absolute; + } diff --git a/test/options/sort-order-fallback/process/css/test-3.expected.css b/test/options/sort-order-fallback/process/css/test-3.expected.css new file mode 100644 index 00000000..8e312988 --- /dev/null +++ b/test/options/sort-order-fallback/process/css/test-3.expected.css @@ -0,0 +1,11 @@ +a { + top: 0; + left: 0; + + color: tomato; + font-size: 1em; + position: absolute; + -webkit-float: left; + -moz-color: zebra; + float: right; + } diff --git a/test/options/sort-order-fallback/process/css/test.css b/test/options/sort-order-fallback/process/css/test.css new file mode 100644 index 00000000..d57de84a --- /dev/null +++ b/test/options/sort-order-fallback/process/css/test.css @@ -0,0 +1,10 @@ +a { + color: tomato; + font-size: 1em; + top: 0; + position: absolute; + left: 0; + -webkit-float: left; + -moz-color: zebra; + float: right; + } diff --git a/test/options/sort-order-fallback/process/css/test.expected.css b/test/options/sort-order-fallback/process/css/test.expected.css new file mode 100644 index 00000000..ec1c2a8f --- /dev/null +++ b/test/options/sort-order-fallback/process/css/test.expected.css @@ -0,0 +1,12 @@ +a { + top: 0; + left: 0; + + -moz-color: zebra; + -webkit-float: left; + float: right; + font-size: 1em; + position: absolute; + + color: tomato; + } diff --git a/test/options/sort-order-fallback/process/test.js b/test/options/sort-order-fallback/process/test.js new file mode 100644 index 00000000..9127d943 --- /dev/null +++ b/test/options/sort-order-fallback/process/test.js @@ -0,0 +1,44 @@ +let Test = require('../../option_test'); + +describe('Option `sort-order-fallback`, process', function() { + describe('css', function() { + it('Should sort leftovers alphabetically if `sort-order-fallback` is set', function() { + let config = { + 'sort-order-fallback': 'abc', + 'sort-order': [ + ['top', 'left'], + ['...'], + ['color'] + ] + }; + let test = new Test(this, config); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Should sort unknown properties alphabetically if `sort-order-fallback` is set', function() { + let config = { + 'sort-order-fallback': 'abc', + 'sort-order': ['top', 'left'] + }; + let test = new Test(this, config); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Properties of the same name should stay in the same order', function() { + let config = { + 'sort-order': ['...'], + 'sort-order-fallback': 'abc' + }; + let test = new Test(this, config); + return test.shouldBeEqual('same-property.css'); + }); + + it('Should leave leftovers as is if `sort-order-fallback` is not set', function() { + let config = { + 'sort-order': ['top', 'left'] + }; + let test = new Test(this, config); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + }); +}); diff --git a/test/options/sort-order-less.js b/test/options/sort-order-less.js deleted file mode 100644 index f0c36a5b..00000000 --- a/test/options/sort-order-less.js +++ /dev/null @@ -1,96 +0,0 @@ -describe('options/sort-order (less)', function() { - beforeEach(function() { - this.filename = __filename; - }); - - it('Should sort properties inside rules', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('rule.less', 'rule.expected.less'); - }); - - it('Should sort properties inside nested rules', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('nested-rule-1.less', 'nested-rule-1.expected.less'); - }); - - it('Should sort properties divided by nested rules', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'left', 'color'] - ] }); - this.shouldBeEqual('nested-rule-2.less', 'nested-rule-2.expected.less'); - }); - - it('Should group declarations with proper comments and spaces (single line)', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('comments-1.less', 'comments-1.expected.less'); - }); - - it('Should group declarations with proper comments and spaces (multiple lines). Test 1', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('comments-2.less', 'comments-2.expected.less'); - }); - - it('Should group declarations with proper comments and spaces (multiple lines). Test 2', function() { - this.comb.configure({ 'sort-order': [ - ['$variable', 'color'] - ] }); - this.shouldBeEqual('comments-3.less', 'comments-3.expected.less'); - }); - - it('Should group declarations with proper comments and spaces (multiple lines). Test 3', function() { - this.comb.configure({ 'sort-order': [ - ['$variable', 'color'] - ] }); - this.shouldBeEqual('comments-3.less', 'comments-3.expected.less'); - }); - - it('Should divide properties from different groups with an empty line', function() { - this.comb.configure({ 'sort-order': [ - ['top'], ['color'] - ] }); - this.shouldBeEqual('different-groups.less', 'different-groups.expected.less'); - }); - - it('Should sort variables', function() { - this.comb.configure({ 'sort-order': [ - ['$variable', 'color'] - ] }); - this.shouldBeEqual('variable.less', 'variable.expected.less'); - }); - - it('Should sort imports', function() { - this.comb.configure({ 'sort-order': [ - ['$import', 'color'] - ] }); - this.shouldBeEqual('import.less', 'import.expected.less'); - }); - - it('Should sort included mixins. Test 1', function() { - this.comb.configure({ 'sort-order': [ - ['$include', 'color', 'border-top', 'border-bottom'] - ] }); - this.shouldBeEqual('mixin-1.less', 'mixin-1.expected.less'); - }); - - it('Should sort included mixins. Test 2', function() { - this.comb.configure({ 'sort-order': [ - ['$include', 'top', 'color'] - ] }); - this.shouldBeEqual('mixin-2.less', 'mixin-2.expected.less'); - }); - - it('Should sort included mixins. Test 3', function() { - this.comb.configure({ 'sort-order': [ - ['$include', 'border', 'color'] - ] }); - this.shouldBeEqual('mixin-3.less', 'mixin-3.expected.less'); - }); -}); diff --git a/test/options/sort-order-scss.js b/test/options/sort-order-scss.js deleted file mode 100644 index 92c22c43..00000000 --- a/test/options/sort-order-scss.js +++ /dev/null @@ -1,103 +0,0 @@ -describe('options/sort-order (scss)', function() { - beforeEach(function() { - this.filename = __filename; - }); - - it('Should sort properties inside rules (single line)', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('rule.scss', 'rule.expected.scss'); - }); - - it('Should sort properties inside rules (multiple lines)', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('rule.scss', 'rule.expected.scss'); - }); - - it('Should sort properties inside nested rules', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('nested-rule-1.scss', 'nested-rule-1.expected.scss'); - }); - - it('Should sort properties divided by nested rules', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'left', 'color'] - ] }); - this.shouldBeEqual('nested-rule-2.scss', 'nested-rule-2.expected.scss'); - }); - - it('Should group declarations with proper comments and spaces (multiple lines)', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('comments-1.scss', 'comments-1.expected.scss'); - }); - - it('Should group declarations with proper comments and spaces (single line)', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('comments-2.scss', 'comments-2.expected.scss'); - }); - - it('Should divide properties from different groups with an empty line', function() { - this.comb.configure({ 'sort-order': [ - ['top'], ['color'] - ] }); - this.shouldBeEqual('different-groups.scss', 'different-groups.expected.scss'); - }); - - it('Should sort variables', function() { - this.comb.configure({ 'sort-order': [ - ['$variable', 'color'] - ] }); - this.shouldBeEqual('variable.scss', 'variable.expected.scss'); - }); - - it('Should sort imports', function() { - this.comb.configure({ 'sort-order': [ - ['$import', 'color'] - ] }); - this.shouldBeEqual('import.scss', 'import.expected.scss'); - }); - - it('Should sort @include-s', function() { - this.comb.configure({ 'sort-order': [ - ['$include', 'color'] - ] }); - this.shouldBeEqual('include.scss', 'include.expected.scss'); - }); - - it('Should sort @extend-s', function() { - this.comb.configure({ 'sort-order': [ - ['$include', 'color'] - ] }); - this.shouldBeEqual('extend.scss', 'extend.expected.scss'); - }); - - it('Should sort properties inside blocks passed to mixins', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'color'] - ] }); - this.shouldBeEqual('mixin.scss', 'mixin.expected.scss'); - }); - - it('Should handle properties preceeding rulesets', function() { - this.comb.configure({ 'sort-order': [ - ['top', 'left', 'color'] - ] }); - this.shouldBeEqual('ruleset.scss', 'ruleset.expected.scss'); - }); - - it('Should handle properties preceeding conditions', function() { - this.comb.configure({ 'sort-order': [ - ['font-size', 'display', 'top', 'color'] - ] }); - this.shouldBeEqual('condition.scss', 'condition.expected.scss'); - }); -}); diff --git a/test/options/sort-order-scss/include.expected.scss b/test/options/sort-order-scss/include.expected.scss deleted file mode 100644 index ea381a79..00000000 --- a/test/options/sort-order-scss/include.expected.scss +++ /dev/null @@ -1 +0,0 @@ -div {@include .nani; color: tomato; } diff --git a/test/options/sort-order-scss/include.scss b/test/options/sort-order-scss/include.scss deleted file mode 100644 index c0ed1345..00000000 --- a/test/options/sort-order-scss/include.scss +++ /dev/null @@ -1 +0,0 @@ -div { color: tomato; @include .nani; } diff --git a/test/options/sort-order.js b/test/options/sort-order.js deleted file mode 100644 index df30bf7f..00000000 --- a/test/options/sort-order.js +++ /dev/null @@ -1,85 +0,0 @@ -var assert = require('assert'); - -describe('options/sort-order', function() { - beforeEach(function() { - this.filename = __filename; - }); - - it('Should be in expected order in case properties are not grouped', function() { - this.comb.configure({ 'sort-order': ['position', 'z-index'] }); - this.shouldBeEqual('single-group.css', 'single-group.expected.css'); - }); - - it('Should be in expected order in case of 1 group', function() { - this.comb.configure({ 'sort-order': [ - ['position', 'z-index'] - ] }); - this.shouldBeEqual('single-group.css', 'single-group.expected.css'); - }); - - it('Shuld be in expected order in case of multiple groups', function() { - this.comb.configure({ 'sort-order': [ - ['position', 'z-index'], - ['width', 'height'] - ] }); - this.shouldBeEqual('multiple-groups.css', 'multiple-groups.expected.css'); - - }); - - it('Should work correctly with comments in case of 1 group', function() { - this.comb.configure({ 'sort-order': [ - ['border-bottom', 'font-style'], - ] }); - this.shouldBeEqual('single-group-comments.css', 'single-group-comments.expected.css'); - }); - - it('Should work correctly with comments in case of multiple groups', function() { - this.comb.configure({ 'sort-order': [ - ['margin'], - ['padding'] - ] }); - this.shouldBeEqual('multiple-groups-comments.css', 'multiple-groups-comments.expected.css'); - }); - - it('Should parse semicolons inside data uri correctly', function() { - this.comb.configure({ - 'sort-order': [ - ['position', 'background', 'color'] - ] - }); - this.shouldBeEqual('data-uri.css', 'data-uri.expected.css'); - }); - - it('Should not add more than 1 line between groups', function() { - var input = this.readFile('multiple-groups-2.css'); - var expected = this.readFile('multiple-groups-2.expected.css'); - this.comb.configure({ - 'sort-order': [ - ['top'], ['color'] - ] - }); - - for (var i = 6; i--;) { - input = this.comb.processString(input); - } - assert.equal(input, expected); - }); - - it('Issue 94. Test 1', function() { - var config = this.comb.getConfig('csscomb'); - this.comb.configure(config); - this.shouldBeEqual('issue-94-1.css', 'issue-94-1.expected.css'); - }); - - it('Issue 94. Test 2', function() { - var config = this.comb.getConfig('csscomb'); - this.comb.configure(config); - this.shouldBeEqual('issue-94-2.css', 'issue-94-2.expected.css'); - }); - - it('Issue 94. Test 3', function() { - var config = this.comb.getConfig('csscomb'); - this.comb.configure(config); - this.shouldBeEqual('issue-94-3.css', 'issue-94-3.expected.css'); - }); -}); diff --git a/test/options/sort-order/detect/test.js b/test/options/sort-order/detect/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/sort-order/lint/test.js b/test/options/sort-order/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/sort-order/data-uri.css b/test/options/sort-order/process/css/data-uri.css similarity index 100% rename from test/options/sort-order/data-uri.css rename to test/options/sort-order/process/css/data-uri.css diff --git a/test/options/sort-order/data-uri.expected.css b/test/options/sort-order/process/css/data-uri.expected.css similarity index 100% rename from test/options/sort-order/data-uri.expected.css rename to test/options/sort-order/process/css/data-uri.expected.css diff --git a/test/options/sort-order/issue-94-1.css b/test/options/sort-order/process/css/issue-94-1.css similarity index 100% rename from test/options/sort-order/issue-94-1.css rename to test/options/sort-order/process/css/issue-94-1.css diff --git a/test/options/sort-order/issue-94-1.expected.css b/test/options/sort-order/process/css/issue-94-1.expected.css similarity index 100% rename from test/options/sort-order/issue-94-1.expected.css rename to test/options/sort-order/process/css/issue-94-1.expected.css diff --git a/test/options/sort-order/issue-94-2.css b/test/options/sort-order/process/css/issue-94-2.css similarity index 100% rename from test/options/sort-order/issue-94-2.css rename to test/options/sort-order/process/css/issue-94-2.css diff --git a/test/options/sort-order/issue-94-2.expected.css b/test/options/sort-order/process/css/issue-94-2.expected.css similarity index 100% rename from test/options/sort-order/issue-94-2.expected.css rename to test/options/sort-order/process/css/issue-94-2.expected.css diff --git a/test/options/sort-order/issue-94-3.css b/test/options/sort-order/process/css/issue-94-3.css similarity index 100% rename from test/options/sort-order/issue-94-3.css rename to test/options/sort-order/process/css/issue-94-3.css diff --git a/test/options/sort-order/issue-94-3.expected.css b/test/options/sort-order/process/css/issue-94-3.expected.css similarity index 100% rename from test/options/sort-order/issue-94-3.expected.css rename to test/options/sort-order/process/css/issue-94-3.expected.css diff --git a/test/options/sort-order/process/css/leftovers-1.css b/test/options/sort-order/process/css/leftovers-1.css new file mode 100644 index 00000000..d559d4e3 --- /dev/null +++ b/test/options/sort-order/process/css/leftovers-1.css @@ -0,0 +1,20 @@ +a +{ + leftover: yay; + z-index: 1; + position: absolute; +} + +b +{ + z-index: 2; + leftover: yay; + position: absolute; +} + +c +{ + z-index: 3; + position: absolute; + leftover: yay; +} diff --git a/test/options/sort-order/process/css/leftovers-1.expected.css b/test/options/sort-order/process/css/leftovers-1.expected.css new file mode 100644 index 00000000..145b83db --- /dev/null +++ b/test/options/sort-order/process/css/leftovers-1.expected.css @@ -0,0 +1,23 @@ +a +{ + position: absolute; + z-index: 1; + + leftover: yay; +} + +b +{ + position: absolute; + z-index: 2; + + leftover: yay; +} + +c +{ + position: absolute; + z-index: 3; + + leftover: yay; +} diff --git a/test/options/sort-order/process/css/leftovers-2.css b/test/options/sort-order/process/css/leftovers-2.css new file mode 100644 index 00000000..d559d4e3 --- /dev/null +++ b/test/options/sort-order/process/css/leftovers-2.css @@ -0,0 +1,20 @@ +a +{ + leftover: yay; + z-index: 1; + position: absolute; +} + +b +{ + z-index: 2; + leftover: yay; + position: absolute; +} + +c +{ + z-index: 3; + position: absolute; + leftover: yay; +} diff --git a/test/options/sort-order/process/css/leftovers-2.expected.css b/test/options/sort-order/process/css/leftovers-2.expected.css new file mode 100644 index 00000000..05c82928 --- /dev/null +++ b/test/options/sort-order/process/css/leftovers-2.expected.css @@ -0,0 +1,23 @@ +a +{ + leftover: yay; + + position: absolute; + z-index: 1; +} + +b +{ + leftover: yay; + + position: absolute; + z-index: 2; +} + +c +{ + leftover: yay; + + position: absolute; + z-index: 3; +} diff --git a/test/options/sort-order/process/css/leftovers-3.css b/test/options/sort-order/process/css/leftovers-3.css new file mode 100644 index 00000000..d559d4e3 --- /dev/null +++ b/test/options/sort-order/process/css/leftovers-3.css @@ -0,0 +1,20 @@ +a +{ + leftover: yay; + z-index: 1; + position: absolute; +} + +b +{ + z-index: 2; + leftover: yay; + position: absolute; +} + +c +{ + z-index: 3; + position: absolute; + leftover: yay; +} diff --git a/test/options/sort-order/process/css/leftovers-3.expected.css b/test/options/sort-order/process/css/leftovers-3.expected.css new file mode 100644 index 00000000..9522dc50 --- /dev/null +++ b/test/options/sort-order/process/css/leftovers-3.expected.css @@ -0,0 +1,20 @@ +a +{ + leftover: yay; + position: absolute; + z-index: 1; +} + +b +{ + leftover: yay; + position: absolute; + z-index: 2; +} + +c +{ + leftover: yay; + position: absolute; + z-index: 3; +} diff --git a/test/options/sort-order/process/css/leftovers-4.css b/test/options/sort-order/process/css/leftovers-4.css new file mode 100644 index 00000000..d559d4e3 --- /dev/null +++ b/test/options/sort-order/process/css/leftovers-4.css @@ -0,0 +1,20 @@ +a +{ + leftover: yay; + z-index: 1; + position: absolute; +} + +b +{ + z-index: 2; + leftover: yay; + position: absolute; +} + +c +{ + z-index: 3; + position: absolute; + leftover: yay; +} diff --git a/test/options/sort-order/process/css/leftovers-4.expected.css b/test/options/sort-order/process/css/leftovers-4.expected.css new file mode 100644 index 00000000..874c72a4 --- /dev/null +++ b/test/options/sort-order/process/css/leftovers-4.expected.css @@ -0,0 +1,20 @@ +a +{ + position: absolute; + leftover: yay; + z-index: 1; +} + +b +{ + position: absolute; + leftover: yay; + z-index: 2; +} + +c +{ + position: absolute; + leftover: yay; + z-index: 3; +} diff --git a/test/options/sort-order/process/css/missing-delimiter.css b/test/options/sort-order/process/css/missing-delimiter.css new file mode 100644 index 00000000..cdba2d46 --- /dev/null +++ b/test/options/sort-order/process/css/missing-delimiter.css @@ -0,0 +1,4 @@ +a { + z-index: 2; + position: absolute +} diff --git a/test/options/sort-order/process/css/missing-delimiter.expected.css b/test/options/sort-order/process/css/missing-delimiter.expected.css new file mode 100644 index 00000000..f619ac51 --- /dev/null +++ b/test/options/sort-order/process/css/missing-delimiter.expected.css @@ -0,0 +1,4 @@ +a { + position: absolute; + z-index: 2; +} diff --git a/test/options/sort-order/multiple-groups-2.css b/test/options/sort-order/process/css/multiple-groups-2.css similarity index 100% rename from test/options/sort-order/multiple-groups-2.css rename to test/options/sort-order/process/css/multiple-groups-2.css diff --git a/test/options/sort-order/multiple-groups-2.expected.css b/test/options/sort-order/process/css/multiple-groups-2.expected.css similarity index 100% rename from test/options/sort-order/multiple-groups-2.expected.css rename to test/options/sort-order/process/css/multiple-groups-2.expected.css diff --git a/test/options/sort-order/multiple-groups-comments.css b/test/options/sort-order/process/css/multiple-groups-comments.css similarity index 100% rename from test/options/sort-order/multiple-groups-comments.css rename to test/options/sort-order/process/css/multiple-groups-comments.css diff --git a/test/options/sort-order/multiple-groups-comments.expected.css b/test/options/sort-order/process/css/multiple-groups-comments.expected.css similarity index 100% rename from test/options/sort-order/multiple-groups-comments.expected.css rename to test/options/sort-order/process/css/multiple-groups-comments.expected.css diff --git a/test/options/sort-order/multiple-groups.css b/test/options/sort-order/process/css/multiple-groups.css similarity index 100% rename from test/options/sort-order/multiple-groups.css rename to test/options/sort-order/process/css/multiple-groups.css diff --git a/test/options/sort-order/multiple-groups.expected.css b/test/options/sort-order/process/css/multiple-groups.expected.css similarity index 100% rename from test/options/sort-order/multiple-groups.expected.css rename to test/options/sort-order/process/css/multiple-groups.expected.css diff --git a/test/options/sort-order/single-group-comments.css b/test/options/sort-order/process/css/single-group-comments.css similarity index 100% rename from test/options/sort-order/single-group-comments.css rename to test/options/sort-order/process/css/single-group-comments.css diff --git a/test/options/sort-order/single-group-comments.expected.css b/test/options/sort-order/process/css/single-group-comments.expected.css similarity index 100% rename from test/options/sort-order/single-group-comments.expected.css rename to test/options/sort-order/process/css/single-group-comments.expected.css diff --git a/test/options/sort-order/single-group.css b/test/options/sort-order/process/css/single-group.css similarity index 100% rename from test/options/sort-order/single-group.css rename to test/options/sort-order/process/css/single-group.css diff --git a/test/options/sort-order/single-group.expected.css b/test/options/sort-order/process/css/single-group.expected.css similarity index 100% rename from test/options/sort-order/single-group.expected.css rename to test/options/sort-order/process/css/single-group.expected.css diff --git a/test/options/sort-order-less/comments-1.expected.less b/test/options/sort-order/process/less/comments-1.expected.less similarity index 100% rename from test/options/sort-order-less/comments-1.expected.less rename to test/options/sort-order/process/less/comments-1.expected.less diff --git a/test/options/sort-order-less/comments-1.less b/test/options/sort-order/process/less/comments-1.less similarity index 100% rename from test/options/sort-order-less/comments-1.less rename to test/options/sort-order/process/less/comments-1.less diff --git a/test/options/sort-order-less/comments-2.expected.less b/test/options/sort-order/process/less/comments-2.expected.less similarity index 100% rename from test/options/sort-order-less/comments-2.expected.less rename to test/options/sort-order/process/less/comments-2.expected.less diff --git a/test/options/sort-order-less/comments-2.less b/test/options/sort-order/process/less/comments-2.less similarity index 100% rename from test/options/sort-order-less/comments-2.less rename to test/options/sort-order/process/less/comments-2.less diff --git a/test/options/sort-order-less/comments-3.expected.less b/test/options/sort-order/process/less/comments-3.expected.less similarity index 100% rename from test/options/sort-order-less/comments-3.expected.less rename to test/options/sort-order/process/less/comments-3.expected.less diff --git a/test/options/sort-order-less/comments-3.less b/test/options/sort-order/process/less/comments-3.less similarity index 100% rename from test/options/sort-order-less/comments-3.less rename to test/options/sort-order/process/less/comments-3.less diff --git a/test/options/sort-order-less/comments-4.expected.less b/test/options/sort-order/process/less/comments-4.expected.less similarity index 100% rename from test/options/sort-order-less/comments-4.expected.less rename to test/options/sort-order/process/less/comments-4.expected.less diff --git a/test/options/sort-order-less/comments-4.less b/test/options/sort-order/process/less/comments-4.less similarity index 100% rename from test/options/sort-order-less/comments-4.less rename to test/options/sort-order/process/less/comments-4.less diff --git a/test/options/sort-order-less/different-groups.expected.less b/test/options/sort-order/process/less/different-groups.expected.less similarity index 100% rename from test/options/sort-order-less/different-groups.expected.less rename to test/options/sort-order/process/less/different-groups.expected.less diff --git a/test/options/sort-order-less/different-groups.less b/test/options/sort-order/process/less/different-groups.less similarity index 100% rename from test/options/sort-order-less/different-groups.less rename to test/options/sort-order/process/less/different-groups.less diff --git a/test/options/sort-order/process/less/extend.expected.less b/test/options/sort-order/process/less/extend.expected.less new file mode 100644 index 00000000..82f0e172 --- /dev/null +++ b/test/options/sort-order/process/less/extend.expected.less @@ -0,0 +1,7 @@ +.block { + &:extend(.foo); + color: black; +} +.a:extend(.b) { + color: pink; +} diff --git a/test/options/sort-order/process/less/extend.less b/test/options/sort-order/process/less/extend.less new file mode 100644 index 00000000..2d1fcff9 --- /dev/null +++ b/test/options/sort-order/process/less/extend.less @@ -0,0 +1,7 @@ +.block { + color: black; + &:extend(.foo); +} +.a:extend(.b) { + color: pink; +} diff --git a/test/options/sort-order-less/import.expected.less b/test/options/sort-order/process/less/import.expected.less similarity index 100% rename from test/options/sort-order-less/import.expected.less rename to test/options/sort-order/process/less/import.expected.less diff --git a/test/options/sort-order-less/import.less b/test/options/sort-order/process/less/import.less similarity index 100% rename from test/options/sort-order-less/import.less rename to test/options/sort-order/process/less/import.less diff --git a/test/options/sort-order-less/mixin-1.expected.less b/test/options/sort-order/process/less/mixin-1.expected.less similarity index 100% rename from test/options/sort-order-less/mixin-1.expected.less rename to test/options/sort-order/process/less/mixin-1.expected.less diff --git a/test/options/sort-order-less/mixin-1.less b/test/options/sort-order/process/less/mixin-1.less similarity index 100% rename from test/options/sort-order-less/mixin-1.less rename to test/options/sort-order/process/less/mixin-1.less diff --git a/test/options/sort-order-less/mixin-2.expected.less b/test/options/sort-order/process/less/mixin-2.expected.less similarity index 100% rename from test/options/sort-order-less/mixin-2.expected.less rename to test/options/sort-order/process/less/mixin-2.expected.less diff --git a/test/options/sort-order-less/mixin-2.less b/test/options/sort-order/process/less/mixin-2.less similarity index 100% rename from test/options/sort-order-less/mixin-2.less rename to test/options/sort-order/process/less/mixin-2.less diff --git a/test/options/sort-order-less/mixin-3.expected.less b/test/options/sort-order/process/less/mixin-3.expected.less similarity index 100% rename from test/options/sort-order-less/mixin-3.expected.less rename to test/options/sort-order/process/less/mixin-3.expected.less diff --git a/test/options/sort-order-less/mixin-3.less b/test/options/sort-order/process/less/mixin-3.less similarity index 100% rename from test/options/sort-order-less/mixin-3.less rename to test/options/sort-order/process/less/mixin-3.less diff --git a/test/options/sort-order/process/less/mixin-4.expected.less b/test/options/sort-order/process/less/mixin-4.expected.less new file mode 100644 index 00000000..94c24ed9 --- /dev/null +++ b/test/options/sort-order/process/less/mixin-4.expected.less @@ -0,0 +1,9 @@ +.block { + .last; + .hide-text; + + color: black; + + .media("lap"); + .media("palm"); +} diff --git a/test/options/sort-order/process/less/mixin-4.less b/test/options/sort-order/process/less/mixin-4.less new file mode 100644 index 00000000..b405880c --- /dev/null +++ b/test/options/sort-order/process/less/mixin-4.less @@ -0,0 +1,7 @@ +.block { + color: black; + .media("lap"); + .media("palm"); + .last; + .hide-text; +} diff --git a/test/options/sort-order-less/nested-rule-1.expected.less b/test/options/sort-order/process/less/nested-rule-1.expected.less similarity index 100% rename from test/options/sort-order-less/nested-rule-1.expected.less rename to test/options/sort-order/process/less/nested-rule-1.expected.less diff --git a/test/options/sort-order-less/nested-rule-1.less b/test/options/sort-order/process/less/nested-rule-1.less similarity index 100% rename from test/options/sort-order-less/nested-rule-1.less rename to test/options/sort-order/process/less/nested-rule-1.less diff --git a/test/options/sort-order-less/nested-rule-2.expected.less b/test/options/sort-order/process/less/nested-rule-2.expected.less similarity index 100% rename from test/options/sort-order-less/nested-rule-2.expected.less rename to test/options/sort-order/process/less/nested-rule-2.expected.less diff --git a/test/options/sort-order-less/nested-rule-2.less b/test/options/sort-order/process/less/nested-rule-2.less similarity index 100% rename from test/options/sort-order-less/nested-rule-2.less rename to test/options/sort-order/process/less/nested-rule-2.less diff --git a/test/options/sort-order-less/rule.expected.less b/test/options/sort-order/process/less/rule.expected.less similarity index 100% rename from test/options/sort-order-less/rule.expected.less rename to test/options/sort-order/process/less/rule.expected.less diff --git a/test/options/sort-order-less/rule.less b/test/options/sort-order/process/less/rule.less similarity index 100% rename from test/options/sort-order-less/rule.less rename to test/options/sort-order/process/less/rule.less diff --git a/test/options/sort-order-less/variable.expected.less b/test/options/sort-order/process/less/variable.expected.less similarity index 100% rename from test/options/sort-order-less/variable.expected.less rename to test/options/sort-order/process/less/variable.expected.less diff --git a/test/options/sort-order-less/variable.less b/test/options/sort-order/process/less/variable.less similarity index 100% rename from test/options/sort-order-less/variable.less rename to test/options/sort-order/process/less/variable.less diff --git a/test/options/sort-order/process/sass/comments.expected.sass b/test/options/sort-order/process/sass/comments.expected.sass new file mode 100644 index 00000000..a7986421 --- /dev/null +++ b/test/options/sort-order/process/sass/comments.expected.sass @@ -0,0 +1,6 @@ +div + /* 2 */ + /* 3 */ + top: 0 // 4 + color: tomato // 1 + /* 5 */ diff --git a/test/options/sort-order/process/sass/comments.sass b/test/options/sort-order/process/sass/comments.sass new file mode 100644 index 00000000..5f3f7cc7 --- /dev/null +++ b/test/options/sort-order/process/sass/comments.sass @@ -0,0 +1,6 @@ +div + color: tomato // 1 + /* 2 */ + /* 3 */ + top: 0 // 4 + /* 5 */ diff --git a/test/options/sort-order/process/sass/condition.expected.sass b/test/options/sort-order/process/sass/condition.expected.sass new file mode 100644 index 00000000..8ffeb9f6 --- /dev/null +++ b/test/options/sort-order/process/sass/condition.expected.sass @@ -0,0 +1,6 @@ +div + top: 0 + color: tomato + @if $color == tomato + font-size: 2px + display: block diff --git a/test/options/sort-order/process/sass/condition.sass b/test/options/sort-order/process/sass/condition.sass new file mode 100644 index 00000000..66379eea --- /dev/null +++ b/test/options/sort-order/process/sass/condition.sass @@ -0,0 +1,6 @@ +div + color: tomato + @if $color == tomato + display: block + font-size: 2px + top: 0 diff --git a/test/options/sort-order/process/sass/different-groups.expected.sass b/test/options/sort-order/process/sass/different-groups.expected.sass new file mode 100644 index 00000000..2ffd1993 --- /dev/null +++ b/test/options/sort-order/process/sass/different-groups.expected.sass @@ -0,0 +1,4 @@ +div + top: 0 + + color: tomato diff --git a/test/options/sort-order/process/sass/different-groups.sass b/test/options/sort-order/process/sass/different-groups.sass new file mode 100644 index 00000000..6d835e28 --- /dev/null +++ b/test/options/sort-order/process/sass/different-groups.sass @@ -0,0 +1,3 @@ +div + color: tomato + top: 0 diff --git a/test/options/sort-order/process/sass/extend.expected.sass b/test/options/sort-order/process/sass/extend.expected.sass new file mode 100644 index 00000000..62ecc2e7 --- /dev/null +++ b/test/options/sort-order/process/sass/extend.expected.sass @@ -0,0 +1,3 @@ +div + @extend %nani + color: tomato diff --git a/test/options/sort-order/process/sass/extend.sass b/test/options/sort-order/process/sass/extend.sass new file mode 100644 index 00000000..e4e3550d --- /dev/null +++ b/test/options/sort-order/process/sass/extend.sass @@ -0,0 +1,3 @@ +div + color: tomato + @extend %nani diff --git a/test/options/sort-order/process/sass/import.expected.sass b/test/options/sort-order/process/sass/import.expected.sass new file mode 100644 index 00000000..f1f2c6a5 --- /dev/null +++ b/test/options/sort-order/process/sass/import.expected.sass @@ -0,0 +1,3 @@ + div + @import "foo.css" + color: tomato diff --git a/test/options/sort-order/process/sass/import.sass b/test/options/sort-order/process/sass/import.sass new file mode 100644 index 00000000..659f76ee --- /dev/null +++ b/test/options/sort-order/process/sass/import.sass @@ -0,0 +1,3 @@ + div + color: tomato + @import "foo.css" diff --git a/test/options/sort-order/process/sass/include-specified-1.expected.sass b/test/options/sort-order/process/sass/include-specified-1.expected.sass new file mode 100644 index 00000000..dbc734c2 --- /dev/null +++ b/test/options/sort-order/process/sass/include-specified-1.expected.sass @@ -0,0 +1,10 @@ +.block + +last + +hide-text + + color: black + + +media(lap) + color: green + +media(palm) + color: red diff --git a/test/options/sort-order/process/sass/include-specified-1.sass b/test/options/sort-order/process/sass/include-specified-1.sass new file mode 100644 index 00000000..5782d546 --- /dev/null +++ b/test/options/sort-order/process/sass/include-specified-1.sass @@ -0,0 +1,10 @@ +.block + color: black + +media(lap) + color: green + + +media(palm) + color: red + + +last + +hide-text diff --git a/test/options/sort-order/process/sass/include-specified-2.expected.sass b/test/options/sort-order/process/sass/include-specified-2.expected.sass new file mode 100644 index 00000000..75cfdaae --- /dev/null +++ b/test/options/sort-order/process/sass/include-specified-2.expected.sass @@ -0,0 +1,10 @@ +.block + @include last + @include hide-text + + color: black + + @include media(lap) + color: green + @include media(palm) + color: red diff --git a/test/options/sort-order/process/sass/include-specified-2.sass b/test/options/sort-order/process/sass/include-specified-2.sass new file mode 100644 index 00000000..9ca4de9c --- /dev/null +++ b/test/options/sort-order/process/sass/include-specified-2.sass @@ -0,0 +1,10 @@ +.block + color: black + @include media(lap) + color: green + + @include media(palm) + color: red + + @include last + @include hide-text diff --git a/test/options/sort-order/process/sass/include.expected.sass b/test/options/sort-order/process/sass/include.expected.sass new file mode 100644 index 00000000..86ab0463 --- /dev/null +++ b/test/options/sort-order/process/sass/include.expected.sass @@ -0,0 +1,3 @@ +div + @include nani + color: tomato diff --git a/test/options/sort-order/process/sass/include.sass b/test/options/sort-order/process/sass/include.sass new file mode 100644 index 00000000..5b42a65f --- /dev/null +++ b/test/options/sort-order/process/sass/include.sass @@ -0,0 +1,3 @@ +div + color: tomato + @include nani diff --git a/test/options/sort-order/process/sass/issue-332-2.expected.sass b/test/options/sort-order/process/sass/issue-332-2.expected.sass new file mode 100644 index 00000000..2c86fb96 --- /dev/null +++ b/test/options/sort-order/process/sass/issue-332-2.expected.sass @@ -0,0 +1,46 @@ +$contest_color: #ffc33c + +.winners_images_button + display: none +.winners_images + height: 100px + padding-left: 0.6% + padding-right: 0.6% + width: 100% + .winner_cell + float: left + height: 100px + width: 85px + .winner_avatar + background-repeat: no-repeat + background-size: 100% + border: 1px solid lightgray + float: left + height: 75px + width: 85px + .winner_year + color: black + float: left + font-family: bold_mic_font + font-size: 17px + height: 20px + margin-bottom: 5px + text-align: center + width: 85px + span + display: none + .winner_info + display: none + .winner_cell:hover + .winner_year + span + display: block + .tooltip-inner + font-size: 10pt + text-align: justify + h6 + color: white + font-family: bold_mic_font + font-size: 12pt + margin: 0 + text-align: center diff --git a/test/options/sort-order/process/sass/issue-332-2.sass b/test/options/sort-order/process/sass/issue-332-2.sass new file mode 100644 index 00000000..d926fa86 --- /dev/null +++ b/test/options/sort-order/process/sass/issue-332-2.sass @@ -0,0 +1,46 @@ +$contest_color: #ffc33c + +.winners_images_button + display: none +.winners_images + width: 100% + padding-left: 0.6% + padding-right: 0.6% + height: 100px + .winner_cell + width: 85px + height: 100px + float: left + .winner_avatar + width: 85px + height: 75px + border: 1px solid lightgray + float: left + background-repeat: no-repeat + background-size: 100% + .winner_year + margin-bottom: 5px + text-align: center + float: left + width: 85px + height: 20px + font-size: 17px + color: black + font-family: bold_mic_font + span + display: none + .winner_info + display: none + .winner_cell:hover + .winner_year + span + display: block + .tooltip-inner + h6 + text-align: center + margin: 0 + font-family: bold_mic_font + color: white + font-size: 12pt + font-size: 10pt + text-align: justify diff --git a/test/options/sort-order/process/sass/issue-332.expected.sass b/test/options/sort-order/process/sass/issue-332.expected.sass new file mode 100644 index 00000000..390beee6 --- /dev/null +++ b/test/options/sort-order/process/sass/issue-332.expected.sass @@ -0,0 +1,9 @@ +.block + color: black + + +media(lap) + color: green + +media(palm) + color: red + +last + +hide-text diff --git a/test/options/sort-order/process/sass/issue-332.sass b/test/options/sort-order/process/sass/issue-332.sass new file mode 100644 index 00000000..3eee3b09 --- /dev/null +++ b/test/options/sort-order/process/sass/issue-332.sass @@ -0,0 +1,11 @@ +.block + +media(lap) + color: green + + +media(palm) + color: red + + color: black + + +last + +hide-text diff --git a/test/options/sort-order/process/sass/mixin.expected.sass b/test/options/sort-order/process/sass/mixin.expected.sass new file mode 100644 index 00000000..e4410624 --- /dev/null +++ b/test/options/sort-order/process/sass/mixin.expected.sass @@ -0,0 +1,4 @@ +.foo + @include nani + top: 0 + color: tomato diff --git a/test/options/sort-order/process/sass/mixin.sass b/test/options/sort-order/process/sass/mixin.sass new file mode 100644 index 00000000..d22987c8 --- /dev/null +++ b/test/options/sort-order/process/sass/mixin.sass @@ -0,0 +1,4 @@ +.foo + @include nani + color: tomato + top: 0 diff --git a/test/options/sort-order/process/sass/nested-rule-1.expected.sass b/test/options/sort-order/process/sass/nested-rule-1.expected.sass new file mode 100644 index 00000000..101521f8 --- /dev/null +++ b/test/options/sort-order/process/sass/nested-rule-1.expected.sass @@ -0,0 +1,5 @@ +div + color: tomato + a + top: 0 + color: nani diff --git a/test/options/sort-order/process/sass/nested-rule-1.sass b/test/options/sort-order/process/sass/nested-rule-1.sass new file mode 100644 index 00000000..3394a7c1 --- /dev/null +++ b/test/options/sort-order/process/sass/nested-rule-1.sass @@ -0,0 +1,5 @@ +div + color: tomato + a + color: nani + top: 0 diff --git a/test/options/sort-order/process/sass/nested-rule-2.expected.sass b/test/options/sort-order/process/sass/nested-rule-2.expected.sass new file mode 100644 index 00000000..54a0fb80 --- /dev/null +++ b/test/options/sort-order/process/sass/nested-rule-2.expected.sass @@ -0,0 +1,6 @@ +div + left: 0 + color: tomato + a + top: 0 + color: nani diff --git a/test/options/sort-order/process/sass/nested-rule-2.sass b/test/options/sort-order/process/sass/nested-rule-2.sass new file mode 100644 index 00000000..9815840e --- /dev/null +++ b/test/options/sort-order/process/sass/nested-rule-2.sass @@ -0,0 +1,6 @@ +div + color: tomato + a + color: nani + top: 0 + left: 0 diff --git a/test/options/sort-order/process/sass/rule.expected.sass b/test/options/sort-order/process/sass/rule.expected.sass new file mode 100644 index 00000000..c6e1f549 --- /dev/null +++ b/test/options/sort-order/process/sass/rule.expected.sass @@ -0,0 +1,3 @@ +div + top: 0 + color: tomato diff --git a/test/options/sort-order/process/sass/rule.sass b/test/options/sort-order/process/sass/rule.sass new file mode 100644 index 00000000..6d835e28 --- /dev/null +++ b/test/options/sort-order/process/sass/rule.sass @@ -0,0 +1,3 @@ +div + color: tomato + top: 0 diff --git a/test/options/sort-order/process/sass/ruleset.expected.sass b/test/options/sort-order/process/sass/ruleset.expected.sass new file mode 100644 index 00000000..54a0fb80 --- /dev/null +++ b/test/options/sort-order/process/sass/ruleset.expected.sass @@ -0,0 +1,6 @@ +div + left: 0 + color: tomato + a + top: 0 + color: nani diff --git a/test/options/sort-order/process/sass/ruleset.sass b/test/options/sort-order/process/sass/ruleset.sass new file mode 100644 index 00000000..9815840e --- /dev/null +++ b/test/options/sort-order/process/sass/ruleset.sass @@ -0,0 +1,6 @@ +div + color: tomato + a + color: nani + top: 0 + left: 0 diff --git a/test/options/sort-order/process/sass/variable.expected.sass b/test/options/sort-order/process/sass/variable.expected.sass new file mode 100644 index 00000000..c7c3634f --- /dev/null +++ b/test/options/sort-order/process/sass/variable.expected.sass @@ -0,0 +1,3 @@ +div + $red: tomato + color: $tomato diff --git a/test/options/sort-order/process/sass/variable.sass b/test/options/sort-order/process/sass/variable.sass new file mode 100644 index 00000000..31bc5a3c --- /dev/null +++ b/test/options/sort-order/process/sass/variable.sass @@ -0,0 +1,3 @@ +div + color: $tomato + $red: tomato diff --git a/test/options/sort-order-scss/comments-1.expected.scss b/test/options/sort-order/process/scss/comments-1.expected.scss similarity index 100% rename from test/options/sort-order-scss/comments-1.expected.scss rename to test/options/sort-order/process/scss/comments-1.expected.scss diff --git a/test/options/sort-order-scss/comments-1.scss b/test/options/sort-order/process/scss/comments-1.scss similarity index 100% rename from test/options/sort-order-scss/comments-1.scss rename to test/options/sort-order/process/scss/comments-1.scss diff --git a/test/options/sort-order-scss/comments-2.expected.scss b/test/options/sort-order/process/scss/comments-2.expected.scss similarity index 100% rename from test/options/sort-order-scss/comments-2.expected.scss rename to test/options/sort-order/process/scss/comments-2.expected.scss diff --git a/test/options/sort-order-scss/comments-2.scss b/test/options/sort-order/process/scss/comments-2.scss similarity index 100% rename from test/options/sort-order-scss/comments-2.scss rename to test/options/sort-order/process/scss/comments-2.scss diff --git a/test/options/sort-order-scss/condition.expected.scss b/test/options/sort-order/process/scss/condition.expected.scss similarity index 100% rename from test/options/sort-order-scss/condition.expected.scss rename to test/options/sort-order/process/scss/condition.expected.scss diff --git a/test/options/sort-order-scss/condition.scss b/test/options/sort-order/process/scss/condition.scss similarity index 100% rename from test/options/sort-order-scss/condition.scss rename to test/options/sort-order/process/scss/condition.scss diff --git a/test/options/sort-order-scss/different-groups.expected.scss b/test/options/sort-order/process/scss/different-groups.expected.scss similarity index 100% rename from test/options/sort-order-scss/different-groups.expected.scss rename to test/options/sort-order/process/scss/different-groups.expected.scss diff --git a/test/options/sort-order-scss/different-groups.scss b/test/options/sort-order/process/scss/different-groups.scss similarity index 100% rename from test/options/sort-order-scss/different-groups.scss rename to test/options/sort-order/process/scss/different-groups.scss diff --git a/test/options/sort-order-scss/extend.expected.scss b/test/options/sort-order/process/scss/extend.expected.scss similarity index 100% rename from test/options/sort-order-scss/extend.expected.scss rename to test/options/sort-order/process/scss/extend.expected.scss diff --git a/test/options/sort-order-scss/extend.scss b/test/options/sort-order/process/scss/extend.scss similarity index 100% rename from test/options/sort-order-scss/extend.scss rename to test/options/sort-order/process/scss/extend.scss diff --git a/test/options/sort-order-scss/import.expected.scss b/test/options/sort-order/process/scss/import.expected.scss similarity index 100% rename from test/options/sort-order-scss/import.expected.scss rename to test/options/sort-order/process/scss/import.expected.scss diff --git a/test/options/sort-order-scss/import.scss b/test/options/sort-order/process/scss/import.scss similarity index 100% rename from test/options/sort-order-scss/import.scss rename to test/options/sort-order/process/scss/import.scss diff --git a/test/options/sort-order/process/scss/include-specified.expected.scss b/test/options/sort-order/process/scss/include-specified.expected.scss new file mode 100644 index 00000000..412f47d2 --- /dev/null +++ b/test/options/sort-order/process/scss/include-specified.expected.scss @@ -0,0 +1,13 @@ +.block { + @include last; + @include hide-text; + + color: black; + + @include media("lap") { + color: green; + } + @include media("palm") { + color: red; + } +} diff --git a/test/options/sort-order/process/scss/include-specified.scss b/test/options/sort-order/process/scss/include-specified.scss new file mode 100644 index 00000000..40b8c98e --- /dev/null +++ b/test/options/sort-order/process/scss/include-specified.scss @@ -0,0 +1,11 @@ +.block { + color: black; + @include media("lap") { + color: green; + } + @include media("palm") { + color: red; + } + @include last; + @include hide-text; +} diff --git a/test/options/sort-order/process/scss/include.expected.scss b/test/options/sort-order/process/scss/include.expected.scss new file mode 100644 index 00000000..390cd58f --- /dev/null +++ b/test/options/sort-order/process/scss/include.expected.scss @@ -0,0 +1 @@ +div {@include nani; color: tomato; } diff --git a/test/options/sort-order/process/scss/include.scss b/test/options/sort-order/process/scss/include.scss new file mode 100644 index 00000000..2feaaec8 --- /dev/null +++ b/test/options/sort-order/process/scss/include.scss @@ -0,0 +1 @@ +div { color: tomato; @include nani; } diff --git a/test/options/sort-order/process/scss/issue-317.scss b/test/options/sort-order/process/scss/issue-317.scss new file mode 100644 index 00000000..f3589f42 --- /dev/null +++ b/test/options/sort-order/process/scss/issue-317.scss @@ -0,0 +1,31 @@ +.tv-header-reminders { + display: inline-block; + position: relative; + vertical-align: top; + + &__spin { + background-color: #fff; + display: none; + height: 100%; + left: 0; + position: absolute; + top: 0; + width: 100%; + + .spin { + height: 100%; + text-align: center; + } + + .spin__icon { + margin-top: 4px; + } + } + + &_loading_yes .tv-header-reminders__spin { + display: block; + + + } + +} diff --git a/test/options/sort-order/process/scss/issue-333.scss b/test/options/sort-order/process/scss/issue-333.scss new file mode 100644 index 00000000..28a52fde --- /dev/null +++ b/test/options/sort-order/process/scss/issue-333.scss @@ -0,0 +1,5 @@ +@mixin opacity($opacity) { + $opacity-ie: $opacity * 100; + opacity: $opacity; + filter: alpha(opacity=50); +} diff --git a/test/options/sort-order/process/scss/issue-399.expected.scss b/test/options/sort-order/process/scss/issue-399.expected.scss new file mode 100644 index 00000000..bf69f21b --- /dev/null +++ b/test/options/sort-order/process/scss/issue-399.expected.scss @@ -0,0 +1,14 @@ +.block { + @extend .hey; + @extend %ho; + color: black; + + @include media("lap") { + color: green; + } + @include media("palm") { + color: red; + } + @include last; + @include hide-text; +} diff --git a/test/options/sort-order/process/scss/issue-399.scss b/test/options/sort-order/process/scss/issue-399.scss new file mode 100644 index 00000000..7e090c81 --- /dev/null +++ b/test/options/sort-order/process/scss/issue-399.scss @@ -0,0 +1,15 @@ +.block { + color: black; + + @extend .hey; + @extend %ho; + + @include media("lap") { + color: green; + } + @include media("palm") { + color: red; + } + @include last; + @include hide-text; +} diff --git a/test/options/sort-order/process/scss/leftovers.expected.scss b/test/options/sort-order/process/scss/leftovers.expected.scss new file mode 100644 index 00000000..d431a118 --- /dev/null +++ b/test/options/sort-order/process/scss/leftovers.expected.scss @@ -0,0 +1,36 @@ +a { + $red: tomato; + + position: $tomato; + + leftover: yay; + border: 1px solid; + + @include nani; + + font: 20px/16px Arial, sans-serif; +} +b { + $red: tomato; + + position: $tomato; + + leftover: yay; + border: 1px solid; + + @include nani; + + font: 20px/16px Arial, sans-serif; +} +c { + $red: tomato; + + position: $tomato; + + leftover: yay; + border: 1px solid; + + @include nani; + + font: 20px/16px Arial, sans-serif; +} diff --git a/test/options/sort-order/process/scss/leftovers.scss b/test/options/sort-order/process/scss/leftovers.scss new file mode 100644 index 00000000..ff5bc4df --- /dev/null +++ b/test/options/sort-order/process/scss/leftovers.scss @@ -0,0 +1,24 @@ +a { + leftover: yay; + border: 1px solid; + @include nani; + font: 20px/16px Arial, sans-serif; + position: $tomato; + $red: tomato; +} +b { + border: 1px solid; + @include nani; + position: $tomato; + leftover: yay; + font: 20px/16px Arial, sans-serif; + $red: tomato; +} +c { + border: 1px solid; + @include nani; + font: 20px/16px Arial, sans-serif; + position: $tomato; + $red: tomato; + leftover: yay; +} diff --git a/test/options/sort-order-scss/mixin.expected.scss b/test/options/sort-order/process/scss/mixin.expected.scss similarity index 100% rename from test/options/sort-order-scss/mixin.expected.scss rename to test/options/sort-order/process/scss/mixin.expected.scss diff --git a/test/options/sort-order-scss/mixin.scss b/test/options/sort-order/process/scss/mixin.scss similarity index 100% rename from test/options/sort-order-scss/mixin.scss rename to test/options/sort-order/process/scss/mixin.scss diff --git a/test/options/sort-order-scss/nested-rule-1.expected.scss b/test/options/sort-order/process/scss/nested-rule-1.expected.scss similarity index 100% rename from test/options/sort-order-scss/nested-rule-1.expected.scss rename to test/options/sort-order/process/scss/nested-rule-1.expected.scss diff --git a/test/options/sort-order-scss/nested-rule-1.scss b/test/options/sort-order/process/scss/nested-rule-1.scss similarity index 100% rename from test/options/sort-order-scss/nested-rule-1.scss rename to test/options/sort-order/process/scss/nested-rule-1.scss diff --git a/test/options/sort-order-scss/nested-rule-2.expected.scss b/test/options/sort-order/process/scss/nested-rule-2.expected.scss similarity index 100% rename from test/options/sort-order-scss/nested-rule-2.expected.scss rename to test/options/sort-order/process/scss/nested-rule-2.expected.scss diff --git a/test/options/sort-order-scss/nested-rule-2.scss b/test/options/sort-order/process/scss/nested-rule-2.scss similarity index 100% rename from test/options/sort-order-scss/nested-rule-2.scss rename to test/options/sort-order/process/scss/nested-rule-2.scss diff --git a/test/options/sort-order-scss/rule-multiline.expected.scss b/test/options/sort-order/process/scss/rule-multiline.expected.scss similarity index 100% rename from test/options/sort-order-scss/rule-multiline.expected.scss rename to test/options/sort-order/process/scss/rule-multiline.expected.scss diff --git a/test/options/sort-order-scss/rule-multiline.scss b/test/options/sort-order/process/scss/rule-multiline.scss similarity index 100% rename from test/options/sort-order-scss/rule-multiline.scss rename to test/options/sort-order/process/scss/rule-multiline.scss diff --git a/test/options/sort-order-scss/rule.expected.scss b/test/options/sort-order/process/scss/rule.expected.scss similarity index 100% rename from test/options/sort-order-scss/rule.expected.scss rename to test/options/sort-order/process/scss/rule.expected.scss diff --git a/test/options/sort-order-scss/rule.scss b/test/options/sort-order/process/scss/rule.scss similarity index 100% rename from test/options/sort-order-scss/rule.scss rename to test/options/sort-order/process/scss/rule.scss diff --git a/test/options/sort-order-scss/ruleset.expected.scss b/test/options/sort-order/process/scss/ruleset.expected.scss similarity index 100% rename from test/options/sort-order-scss/ruleset.expected.scss rename to test/options/sort-order/process/scss/ruleset.expected.scss diff --git a/test/options/sort-order-scss/ruleset.scss b/test/options/sort-order/process/scss/ruleset.scss similarity index 100% rename from test/options/sort-order-scss/ruleset.scss rename to test/options/sort-order/process/scss/ruleset.scss diff --git a/test/options/sort-order-scss/variable.expected.scss b/test/options/sort-order/process/scss/variable.expected.scss similarity index 100% rename from test/options/sort-order-scss/variable.expected.scss rename to test/options/sort-order/process/scss/variable.expected.scss diff --git a/test/options/sort-order-scss/variable.scss b/test/options/sort-order/process/scss/variable.scss similarity index 100% rename from test/options/sort-order-scss/variable.scss rename to test/options/sort-order/process/scss/variable.scss diff --git a/test/options/sort-order/process/test.js b/test/options/sort-order/process/test.js new file mode 100644 index 00000000..6df33154 --- /dev/null +++ b/test/options/sort-order/process/test.js @@ -0,0 +1,493 @@ +var assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `sort-order`, process', function() { + describe('css', function() { + it('Should be in expected order in case properties are not grouped', function() { + let test = new Test(this, {'sort-order': ['position', 'z-index']}); + return test.shouldBeEqual('single-group.css', 'single-group.expected.css'); + }); + + it('Should be in expected order in case of 1 group', function() { + let test = new Test(this, {'sort-order': [['position', 'z-index']]}); + return test.shouldBeEqual('single-group.css', 'single-group.expected.css'); + }); + + it('Shuld be in expected order in case of multiple groups', function() { + let test = new Test(this, { + 'sort-order': [ + ['position', 'z-index'], + ['width', 'height'] + ] + }); + return test.shouldBeEqual('multiple-groups.css', 'multiple-groups.expected.css'); + }); + + it('Should work correctly with comments in case of 1 group', function() { + let test = new Test(this, { + 'sort-order': [ + ['border-bottom', 'font-style'], + ] + }); + return test.shouldBeEqual('single-group-comments.css', 'single-group-comments.expected.css'); + }); + + it('Should work correctly with comments in case of multiple groups', function() { + let test = new Test(this, { + 'sort-order': [ + ['margin'], ['padding'] + ] + }); + return test.shouldBeEqual('multiple-groups-comments.css', 'multiple-groups-comments.expected.css'); + }); + + it('Should parse semicolons inside data uri correctly', function() { + let test = new Test(this, { + 'sort-order': [ + ['position', 'background', 'color'] + ] + }); + return test.shouldBeEqual('data-uri.css', 'data-uri.expected.css'); + }); + + it('Should not add more than 1 line between groups', function() { + let test = new Test(this, { + 'sort-order': [ + ['top'], ['color'] + ] + }); + let input = test.readFile('multiple-groups-2.css'); + let expected = test.readFile('multiple-groups-2.expected.css'); + + test.comb.processString(input) + .then(test.comb.processString) + .then(test.comb.processString) + .then(test.comb.processString) + .then(test.comb.processString) + .then(test.comb.processString) + .then(function(actual) { + assert.equal(actual, expected); + }); + }); + + it('Issue 94. Test 1', function() { + let test = new Test(this); + test.useConfig('csscomb'); + return test.shouldBeEqual('issue-94-1.css', 'issue-94-1.expected.css'); + }); + + it('Issue 94. Test 2', function() { + let test = new Test(this); + test.useConfig('csscomb'); + return test.shouldBeEqual('issue-94-2.css', 'issue-94-2.expected.css'); + }); + + it('Issue 94. Test 3', function() { + let test = new Test(this); + test.useConfig('csscomb'); + return test.shouldBeEqual('issue-94-3.css', 'issue-94-3.expected.css'); + }); + + it('Should place the leftovers in the end', function() { + let test = new Test(this); + test.useConfig('csscomb'); + return test.shouldBeEqual('leftovers-1.css', 'leftovers-1.expected.css'); + }); + + it('Should place the leftovers in the beginning', function() { + let test = new Test(this); + let config = test.Comb.getConfig('csscomb'); + config['sort-order'][0].unshift(['...']); + test.comb.configure(config); + + return test.shouldBeEqual('leftovers-2.css', 'leftovers-2.expected.css') + .then(function() { + config['sort-order'][0].shift(); + }); + }); + + it('Should place the leftovers in the beginning of its group', function() { + let test = new Test(this); + let config = test.Comb.getConfig('csscomb'); + config['sort-order'][1].unshift('...'); + test.comb.configure(config); + return test.shouldBeEqual('leftovers-3.css', 'leftovers-3.expected.css') + .then(function() { + config['sort-order'][1].shift(); + }); + }); + + it('Should place the leftovers in the middle of its group', function() { + let test = new Test(this); + let config = test.Comb.getConfig('csscomb'); + config['sort-order'][1].splice(1, 0, '...'); + test.comb.configure(config); + return test.shouldBeEqual('leftovers-4.css', 'leftovers-4.expected.css') + .then(function() { + config['sort-order'][1].splice(1, 1); + }); + }); + + it('Should add declaration delimiters if they are missing', function() { + let test = new Test(this, {'sort-order': ['position', 'z-index']}); + return test.shouldBeEqual('missing-delimiter.css', 'missing-delimiter.expected.css'); + }); + }); + + describe('less', function() { + it('Should sort properties inside rules', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('rule.less', 'rule.expected.less'); + }); + + it('Should sort properties inside nested rules', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('nested-rule-1.less', 'nested-rule-1.expected.less'); + }); + + it('Should sort properties divided by nested rules', function() { + let test = new Test(this, { + 'sort-order': [['top', 'left', 'color']] + }); + return test.shouldBeEqual('nested-rule-2.less', 'nested-rule-2.expected.less'); + }); + + it('Should group declarations with proper comments and spaces (single line)', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('comments-1.less', 'comments-1.expected.less'); + }); + + it('Should group declarations with proper comments and spaces (multiple lines). Test 1', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('comments-2.less', 'comments-2.expected.less'); + }); + + it('Should group declarations with proper comments and spaces (multiple lines). Test 2', function() { + let test = new Test(this, { + 'sort-order': [['$variable', 'color']] + }); + return test.shouldBeEqual('comments-3.less', 'comments-3.expected.less'); + }); + + it('Should group declarations with proper comments and spaces (multiple lines). Test 3', function() { + let test = new Test(this, { + 'sort-order': [['$variable', 'color']] + }); + return test.shouldBeEqual('comments-3.less', 'comments-3.expected.less'); + }); + + it('Should divide properties from different groups with an empty line', function() { + let test = new Test(this, { + 'sort-order': [['top'], ['color']] + }); + return test.shouldBeEqual('different-groups.less', 'different-groups.expected.less'); + }); + + it('Should sort variables', function() { + let test = new Test(this, { + 'sort-order': [['$variable', 'color']] + }); + return test.shouldBeEqual('variable.less', 'variable.expected.less'); + }); + + it('Should sort imports', function() { + let test = new Test(this, { + 'sort-order': [['$import', 'color']] + }); + return test.shouldBeEqual('import.less', 'import.expected.less'); + }); + + it('Should sort included mixins. Test 1', function() { + let test = new Test(this, { + 'sort-order': [['$include', 'color', 'border-top', 'border-bottom']] + }); + return test.shouldBeEqual('mixin-1.less', 'mixin-1.expected.less'); + }); + + it('Should sort included mixins. Test 2', function() { + let test = new Test(this, { + 'sort-order': [['$include', 'top', 'color']] + }); + return test.shouldBeEqual('mixin-2.less', 'mixin-2.expected.less'); + }); + + it('Should sort included mixins. Test 3', function() { + let test = new Test(this, { + 'sort-order': [['$include', 'border', 'color']] + }); + return test.shouldBeEqual('mixin-3.less', 'mixin-3.expected.less'); + }); + + it('Should sort included mixins with specified name. Test 4', function() { + let test = new Test(this, { + 'sort-order': [['$include'], ['color'], ['$include media']] + }); + return test.shouldBeEqual('mixin-4.less', 'mixin-4.expected.less'); + }); + + it('Should sort @extend-s', function() { + let test = new Test(this, { + 'sort-order': [['$extend', 'color']] + }); + return test.shouldBeEqual('extend.less', 'extend.expected.less'); + }); + }); + + describe('sass', function() { + it('Should sort properties inside rules', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('rule.sass', 'rule.expected.sass'); + }); + + it('Should sort properties inside nested rules', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('nested-rule-1.sass', 'nested-rule-1.expected.sass'); + }); + + it('Should sort properties divided by nested rules', function() { + let test = new Test(this, { + 'sort-order': [['top', 'left', 'color']] + }); + return test.shouldBeEqual('nested-rule-2.sass', 'nested-rule-2.expected.sass'); + }); + + it('Should group declarations with proper comments and spaces (multiple lines)', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('comments.sass', 'comments.expected.sass'); + }); + + it('Should divide properties from different groups with an empty line', function() { + let test = new Test(this, { + 'sort-order': [['top'], ['color']] + }); + return test.shouldBeEqual('different-groups.sass', 'different-groups.expected.sass'); + }); + + it('Should sort variables', function() { + let test = new Test(this, { + 'sort-order': [['$variable', 'color']] + }); + return test.shouldBeEqual('variable.sass', 'variable.expected.sass'); + }); + + it('Should sort imports', function() { + let test = new Test(this, { + 'sort-order': [['$import', 'color']] + }); + return test.shouldBeEqual('import.sass', 'import.expected.sass'); + }); + + it('Should sort @include-s', function() { + let test = new Test(this, { + 'sort-order': [['$include', 'color']] + }); + return test.shouldBeEqual('include.sass', 'include.expected.sass'); + }); + + it('Should sort @extend-s', function() { + let test = new Test(this, { + 'sort-order': [['$extend', 'color']] + }); + return test.shouldBeEqual('extend.sass', 'extend.expected.sass'); + }); + + it('Should sort @include-s with specified name. Test 1', function() { + let test = new Test(this, { + 'sort-order': [['$include'], ['color'], ['$include media']] + }); + return test.shouldBeEqual('include-specified-1.sass', 'include-specified-1.expected.sass'); + }); + + it('Should sort @include-s with specified name. Test 2', function() { + let test = new Test(this, { + 'sort-order': [['$include'], ['color'], ['$include media']] + }); + return test.shouldBeEqual('include-specified-2.sass', 'include-specified-2.expected.sass'); + }); + + it('Should sort properties inside blocks passed to mixins', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('mixin.sass', 'mixin.expected.sass'); + }); + + it('Should handle properties preceeding rulesets', function() { + let test = new Test(this, { + 'sort-order': [['top', 'left', 'color']] + }); + return test.shouldBeEqual('ruleset.sass', 'ruleset.expected.sass'); + }); + + it('Should handle properties preceeding conditions', function() { + let test = new Test(this, { + 'sort-order': [['font-size', 'display', 'top', 'color']] + }); + return test.shouldBeEqual('condition.sass', 'condition.expected.sass'); + }); + + it('Issue 332', function() { + let test = new Test(this, { + 'sort-order': [['color'], ['$include']] + }); + return test.shouldBeEqual('issue-332.sass', 'issue-332.expected.sass'); + }); + + it('Issue 332, test 2', function() { + let test = new Test(this, { + 'sort-order': [['...']], + 'sort-order-fallback': 'abc' + }); + return test.shouldBeEqual('issue-332-2.sass', 'issue-332-2.expected.sass'); + }); + }); + + describe('scss', function() { + it('Should sort properties inside rules (single line)', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('rule.scss', 'rule.expected.scss'); + }); + + it('Should sort properties inside rules (multiple lines)', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('rule.scss', 'rule.expected.scss'); + }); + + it('Should sort properties inside nested rules', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('nested-rule-1.scss', 'nested-rule-1.expected.scss'); + }); + + it('Should sort properties divided by nested rules', function() { + let test = new Test(this, { + 'sort-order': [['top', 'left', 'color']] + }); + return test.shouldBeEqual('nested-rule-2.scss', 'nested-rule-2.expected.scss'); + }); + + it('Should group declarations with proper comments and spaces (multiple lines)', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('comments-1.scss', 'comments-1.expected.scss'); + }); + + it('Should group declarations with proper comments and spaces (single line)', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('comments-2.scss', 'comments-2.expected.scss'); + }); + + it('Should divide properties from different groups with an empty line', function() { + let test = new Test(this, { + 'sort-order': [['top'], ['color']] + }); + return test.shouldBeEqual('different-groups.scss', 'different-groups.expected.scss'); + }); + + it('Should sort variables', function() { + let test = new Test(this, { + 'sort-order': [['$variable', 'color']] + }); + return test.shouldBeEqual('variable.scss', 'variable.expected.scss'); + }); + + it('Should sort imports', function() { + let test = new Test(this, { + 'sort-order': [['$import', 'color']] + }); + return test.shouldBeEqual('import.scss', 'import.expected.scss'); + }); + + it('Should sort @include-s', function() { + let test = new Test(this, { + 'sort-order': [['$include', 'color']] + }); + return test.shouldBeEqual('include.scss', 'include.expected.scss'); + }); + + it('Should sort @include-s with specified name', function() { + let test = new Test(this, { + 'sort-order': [['$include'], ['color'], ['$include media']] + }); + return test.shouldBeEqual('include-specified.scss', 'include-specified.expected.scss'); + }); + + it('Should sort @extend-s', function() { + let test = new Test(this, { + 'sort-order': [['$extend', 'color']] + }); + return test.shouldBeEqual('extend.scss', 'extend.expected.scss'); + }); + + it('Should sort properties inside blocks passed to mixins', function() { + let test = new Test(this, { + 'sort-order': [['top', 'color']] + }); + return test.shouldBeEqual('mixin.scss', 'mixin.expected.scss'); + }); + + it('Should handle properties preceeding rulesets', function() { + let test = new Test(this, { + 'sort-order': [['top', 'left', 'color']] + }); + return test.shouldBeEqual('ruleset.scss', 'ruleset.expected.scss'); + }); + + it('Should handle properties preceeding conditions', function() { + let test = new Test(this, { + 'sort-order': [['font-size', 'display', 'top', 'color']] + }); + return test.shouldBeEqual('condition.scss', 'condition.expected.scss'); + }); + + it('Should sort complex case with leftovers', function() { + let test = new Test(this, { + 'sort-order': [ + ['$variable'], + ['position'], + ['...', 'border'], + ['$include'], + ['font'] + ] + }); + return test.shouldBeEqual('leftovers.scss', 'leftovers.expected.scss'); + }); + + it('Issue 317', function() { + let test = new Test(this, {'sort-order': ['...']}); + return test.shouldBeEqual('issue-317.scss'); + }); + + it('Issue 333', function() { + let test = new Test(this, {'sort-order': ['...']}); + return test.shouldBeEqual('issue-333.scss'); + }); + + it('Issue 399', function() { + let test = new Test(this, {'sort-order': [['$extend', 'color']]}); + return test.shouldBeEqual('issue-399.expected.scss'); + }); + }); +}); diff --git a/test/options/space-after-colon/detect/test.js b/test/options/space-after-colon/detect/test.js new file mode 100644 index 00000000..5f325223 --- /dev/null +++ b/test/options/space-after-colon/detect/test.js @@ -0,0 +1,43 @@ +let Test = require('../../option_test'); + +describe('Option `space-after-colon`, detect', function() { + describe('css', function() { + it('Should detect no whitespaces', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-colon'], + 'a { color:red }', + {'space-after-colon': ''} + ); + }); + + it('Should detect space from two variants', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-colon'], + 'a { color: red; color :red }', + {'space-after-colon': ' '} + ); + }); + + it('Should detect no whitespaces along three variants', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-colon'], + 'a { color: red; background :red } b { width:10px }', + {'space-after-colon': ''} + ); + }); + + it('Should detect space', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-colon'], + 'a { color : red; background :red } b { width: 10px }', + {'space-after-colon': ' '} + ); + }); + }); +}); + + diff --git a/test/options/space-after-colon/lint/test.js b/test/options/space-after-colon/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-after-colon/process/css/test-2.expected.css b/test/options/space-after-colon/process/css/test-2.expected.css new file mode 100644 index 00000000..c1355f35 --- /dev/null +++ b/test/options/space-after-colon/process/css/test-2.expected.css @@ -0,0 +1,5 @@ +a { color: red } +a{color: red} +a {color : red} +a {color : /* foo */ red } +a {color /* bar */ : red } diff --git a/test/options/space-after-colon/process/css/test-3.expected.css b/test/options/space-after-colon/process/css/test-3.expected.css new file mode 100644 index 00000000..63d57dc7 --- /dev/null +++ b/test/options/space-after-colon/process/css/test-3.expected.css @@ -0,0 +1,10 @@ +a { color: + red } +a{color: + red} +a {color : + red} +a {color : + /* foo */ red } +a {color /* bar */ : + red } diff --git a/test/options/space-after-colon/process/css/test.css b/test/options/space-after-colon/process/css/test.css new file mode 100644 index 00000000..637656fb --- /dev/null +++ b/test/options/space-after-colon/process/css/test.css @@ -0,0 +1,5 @@ +a { color: red } +a{color:red} +a {color : red} +a {color : /* foo */ red } +a {color /* bar */ : red } diff --git a/test/options/space-after-colon/process/css/test.expected.css b/test/options/space-after-colon/process/css/test.expected.css new file mode 100644 index 00000000..2d137811 --- /dev/null +++ b/test/options/space-after-colon/process/css/test.expected.css @@ -0,0 +1,5 @@ +a { color:red } +a{color:red} +a {color :red} +a {color :/* foo */ red } +a {color /* bar */ :red } diff --git a/test/options/space-after-colon/process/sass/colon-after-property-name.expected.sass b/test/options/space-after-colon/process/sass/colon-after-property-name.expected.sass new file mode 100644 index 00000000..9b55f4af --- /dev/null +++ b/test/options/space-after-colon/process/sass/colon-after-property-name.expected.sass @@ -0,0 +1,3 @@ +a + color: panda + top: 0 diff --git a/test/options/space-after-colon/process/sass/colon-after-property-name.sass b/test/options/space-after-colon/process/sass/colon-after-property-name.sass new file mode 100644 index 00000000..5e4e236e --- /dev/null +++ b/test/options/space-after-colon/process/sass/colon-after-property-name.sass @@ -0,0 +1,3 @@ +a + color: panda + top:0 diff --git a/test/options/space-after-colon/process/sass/colon-before-property-name.expected.sass b/test/options/space-after-colon/process/sass/colon-before-property-name.expected.sass new file mode 100644 index 00000000..a8a5e3e4 --- /dev/null +++ b/test/options/space-after-colon/process/sass/colon-before-property-name.expected.sass @@ -0,0 +1,3 @@ +a + :color panda + :top 0 diff --git a/test/options/space-after-colon/process/sass/colon-before-property-name.sass b/test/options/space-after-colon/process/sass/colon-before-property-name.sass new file mode 100644 index 00000000..a8a5e3e4 --- /dev/null +++ b/test/options/space-after-colon/process/sass/colon-before-property-name.sass @@ -0,0 +1,3 @@ +a + :color panda + :top 0 diff --git a/test/options/space-after-colon/process/scss/pseudo-elements.expected.scss b/test/options/space-after-colon/process/scss/pseudo-elements.expected.scss new file mode 100644 index 00000000..46b16d28 --- /dev/null +++ b/test/options/space-after-colon/process/scss/pseudo-elements.expected.scss @@ -0,0 +1,11 @@ +li{ + float: left; + + a:hover{ + text-decoration: underline; + } +} + +li:before { + color: tomato; +} diff --git a/test/options/space-after-colon/process/scss/pseudo-elements.scss b/test/options/space-after-colon/process/scss/pseudo-elements.scss new file mode 100644 index 00000000..dbb4acea --- /dev/null +++ b/test/options/space-after-colon/process/scss/pseudo-elements.scss @@ -0,0 +1,11 @@ +li{ + float:left; + + a:hover{ + text-decoration:underline; + } +} + +li:before { + color:tomato; +} diff --git a/test/options/space-after-colon/process/test.js b/test/options/space-after-colon/process/test.js new file mode 100644 index 00000000..84119b16 --- /dev/null +++ b/test/options/space-after-colon/process/test.js @@ -0,0 +1,55 @@ +let Test = require('../../option_test'); + +describe('Option `space-after-colon`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-after-colon': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-after-colon': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-after-colon': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space after colon', function() { + let test = new Test(this, {'space-after-colon': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only)=> should set proper space after colon', function() { + let test = new Test(this, {'space-after-colon': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spacesand newlines)=> should set proper space after colon', function() { + let test = new Test(this, {'space-after-colon': '\n '}); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + }); + + describe('sass', function() { + it('Should set proper space if colon is after property name', function() { + let test = new Test(this, {'space-after-colon': 2}); + return test.shouldBeEqual('colon-after-property-name.sass', 'colon-after-property-name.expected.sass'); + }); + + it('Should not change space after colon which is before property name', function() { + let test = new Test(this, {'space-after-colon': 1}); + return test.shouldBeEqual('colon-before-property-name.sass', 'colon-before-property-name.expected.sass'); + }); + }); + + describe('scss', function() { + it('Space after colon should not affect pseudo elements', function() { + let test = new Test(this, {'space-after-colon': 1}); + return test.shouldBeEqual('pseudo-elements.scss', 'pseudo-elements.expected.scss'); + }); + }); +}); + diff --git a/test/options/space-after-combinator/detect/test.js b/test/options/space-after-combinator/detect/test.js new file mode 100644 index 00000000..6728f664 --- /dev/null +++ b/test/options/space-after-combinator/detect/test.js @@ -0,0 +1,61 @@ +let Test = require('../../option_test'); + +describe('Option `space-after-combinator`, detect', function() { + describe('css', function() { + it('Should detect no whitespaces after combinator', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-combinator'], + 'a+b { color:red }', + {'space-after-combinator': ''} + ); + }); + + it('Should detect a space after combinator', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-combinator'], + 'a + \n b { color:red }', + {'space-after-combinator': ' \n '} + ); + }); + + it('Should detect a space after combinator in long selector', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-combinator'], + 'a + b ~ c>d { color:red }', + {'space-after-combinator': ' '} + ); + }); + + it('Should detect a space after combinator in long selector, test 2', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-combinator'], + 'a>b + c + d { color:red }', + {'space-after-combinator': ' '} + ); + }); + + it('Should detect no whitespaces after combinator in long selector', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-combinator'], + 'a+b ~ c+d { color:red }', + {'space-after-combinator': ''} + ); + }); + + it('Shouldn’t detect whitespaces after combinator in selector without combinators', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-combinator'], + 'a { color:red }', + {} + ); + }); + }); +}); + + diff --git a/test/options/space-after-combinator/lint/test.js b/test/options/space-after-combinator/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-after-combinator/process/css/test-2.expected.css b/test/options/space-after-combinator/process/css/test-2.expected.css new file mode 100644 index 00000000..32df4395 --- /dev/null +++ b/test/options/space-after-combinator/process/css/test-2.expected.css @@ -0,0 +1,10 @@ +a> b { color: red } +a> b { color: red } +a > b { color: red } +a+ b { color: red } +a+ b { color: red } +a + b { color: red } +a~ b { color: red } +a~ b { color: red } +a ~ b { color: red } +a ~ b+ c> d { color: red } diff --git a/test/options/space-after-combinator/process/css/test-3.expected.css b/test/options/space-after-combinator/process/css/test-3.expected.css new file mode 100644 index 00000000..9d34666a --- /dev/null +++ b/test/options/space-after-combinator/process/css/test-3.expected.css @@ -0,0 +1,22 @@ +a> + b { color: red } +a> + b { color: red } +a > + b { color: red } +a+ + b { color: red } +a+ + b { color: red } +a + + b { color: red } +a~ + b { color: red } +a~ + b { color: red } +a ~ + b { color: red } +a ~ + b+ + c> + d { color: red } diff --git a/test/options/space-after-combinator/process/css/test.css b/test/options/space-after-combinator/process/css/test.css new file mode 100644 index 00000000..4ad8d86e --- /dev/null +++ b/test/options/space-after-combinator/process/css/test.css @@ -0,0 +1,10 @@ +a>b { color: red } +a> b { color: red } +a >b { color: red } +a+b { color: red } +a+ b { color: red } +a +b { color: red } +a~b { color: red } +a~ b { color: red } +a ~b { color: red } +a ~b+ c>d { color: red } diff --git a/test/options/space-after-combinator/process/css/test.expected.css b/test/options/space-after-combinator/process/css/test.expected.css new file mode 100644 index 00000000..186958d1 --- /dev/null +++ b/test/options/space-after-combinator/process/css/test.expected.css @@ -0,0 +1,10 @@ +a>b { color: red } +a>b { color: red } +a >b { color: red } +a+b { color: red } +a+b { color: red } +a +b { color: red } +a~b { color: red } +a~b { color: red } +a ~b { color: red } +a ~b+c>d { color: red } diff --git a/test/options/space-after-combinator/process/test.js b/test/options/space-after-combinator/process/test.js new file mode 100644 index 00000000..8af41c68 --- /dev/null +++ b/test/options/space-after-combinator/process/test.js @@ -0,0 +1,35 @@ +let Test = require('../../option_test'); + +describe('Option `space-after-combinator`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-after-combinator': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-after-combinator': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-after-combinator': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space after combinator', function() { + let test = new Test(this, {'space-after-combinator': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space after combinator', function() { + let test = new Test(this, {'space-after-combinator': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space after combinator', function() { + let test = new Test(this, {'space-after-combinator': '\n '}); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + }); +}); diff --git a/test/options/space-after-opening-brace/detect/test.js b/test/options/space-after-opening-brace/detect/test.js new file mode 100644 index 00000000..533cc965 --- /dev/null +++ b/test/options/space-after-opening-brace/detect/test.js @@ -0,0 +1,59 @@ +let Test = require('../../option_test'); + +describe('Option `space-after-opening-brace`, detect', function() { + describe('css', function() { + it('Should detect no whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-opening-brace'], + 'a{top:0}', + {'space-after-opening-brace': ''} + ); + }); + + it('Should detect whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-opening-brace'], + 'a{\n\ttop:0}', + {'space-after-opening-brace': '\n\t'} + ); + }); + + it('Should detect no whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-opening-brace'], + 'a{top:0} b{\n left:0}', + {'space-after-opening-brace': ''} + ); + }); + + it('Should detect whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-opening-brace'], + 'a{ top:0 } b{left:0}', + {'space-after-opening-brace': ' '} + ); + }); + + it('Should detect no whitespace (3 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-opening-brace'], + 'a{top:0} b { left: 0 } c{\n\tright:0}', + {'space-after-opening-brace': ''} + ); + }); + + it('Should detect whitespace (3 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-opening-brace'], + 'a{\ntop:0} b{\nleft:0} c{\n right:0}', + {'space-after-opening-brace': '\n'} + ); + }); + }); +}); diff --git a/test/options/space-after-opening-brace/lint/test.js b/test/options/space-after-opening-brace/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-after-opening-brace/process/css/issue-387.css b/test/options/space-after-opening-brace/process/css/issue-387.css new file mode 100644 index 00000000..3b66e1f1 --- /dev/null +++ b/test/options/space-after-opening-brace/process/css/issue-387.css @@ -0,0 +1 @@ +@media only screen (max-width:479px) {} diff --git a/test/options/space-after-opening-brace/process/css/issue-387.expected.css b/test/options/space-after-opening-brace/process/css/issue-387.expected.css new file mode 100644 index 00000000..aecde5a6 --- /dev/null +++ b/test/options/space-after-opening-brace/process/css/issue-387.expected.css @@ -0,0 +1,2 @@ +@media only screen (max-width:479px) { +} diff --git a/test/options/space-after-opening-brace/process/css/test-2.expected.css b/test/options/space-after-opening-brace/process/css/test-2.expected.css new file mode 100644 index 00000000..a34ef363 --- /dev/null +++ b/test/options/space-after-opening-brace/process/css/test-2.expected.css @@ -0,0 +1,5 @@ +a{ top: 0} +a { top: 0 } + +@media print{ a{ top: 0}} +@media print { a { top: 0 } } diff --git a/test/options/space-after-opening-brace/process/css/test-3.expected.css b/test/options/space-after-opening-brace/process/css/test-3.expected.css new file mode 100644 index 00000000..4c044b43 --- /dev/null +++ b/test/options/space-after-opening-brace/process/css/test-3.expected.css @@ -0,0 +1,11 @@ +a{ + top: 0} +a { + top: 0 } + +@media print{ + a{ + top: 0}} +@media print { + a { + top: 0 } } diff --git a/test/options/space-after-opening-brace/process/css/test.css b/test/options/space-after-opening-brace/process/css/test.css new file mode 100644 index 00000000..7c71281c --- /dev/null +++ b/test/options/space-after-opening-brace/process/css/test.css @@ -0,0 +1,5 @@ +a{top: 0} +a { top: 0 } + +@media print{a{top: 0}} +@media print { a { top: 0 } } diff --git a/test/options/space-after-opening-brace/process/css/test.expected.css b/test/options/space-after-opening-brace/process/css/test.expected.css new file mode 100644 index 00000000..fa761a52 --- /dev/null +++ b/test/options/space-after-opening-brace/process/css/test.expected.css @@ -0,0 +1,5 @@ +a{top: 0} +a {top: 0 } + +@media print{a{top: 0}} +@media print {a {top: 0 } } diff --git a/test/options/space-after-opening-brace/process/test.js b/test/options/space-after-opening-brace/process/test.js new file mode 100644 index 00000000..c65c6841 --- /dev/null +++ b/test/options/space-after-opening-brace/process/test.js @@ -0,0 +1,41 @@ +let Test = require('../../option_test'); + +describe('Option `space-after-opening-brace`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-after-opening-brace': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-after-opening-brace': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-after-opening-brace': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space after {', function() { + let test = new Test(this, {'space-after-opening-brace': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space after {', function() { + let test = new Test(this, {'space-after-opening-brace': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space after {', function() { + let test = new Test(this, {'space-after-opening-brace': '\n '}); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + + it('Issue 387', function() { + let test = new Test(this, {'space-after-opening-brace': '\n'}); + return test.shouldBeEqual('issue-387.css', 'issue-387.expected.css'); + }); + }); +}); + diff --git a/test/options/space-after-selector-delimiter/detect/test.js b/test/options/space-after-selector-delimiter/detect/test.js new file mode 100644 index 00000000..228942f1 --- /dev/null +++ b/test/options/space-after-selector-delimiter/detect/test.js @@ -0,0 +1,59 @@ +let Test = require('../../option_test'); + +describe('Option `space-after-selector-delimiter`, detect', function() { + describe('css', function() { + it('Should detect no whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-selector-delimiter'], + 'a,b{top:0}', + {'space-after-selector-delimiter': ''} + ); + }); + + it('Should detect whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-selector-delimiter'], + 'a, \n b {top:0}', + {'space-after-selector-delimiter': ' \n '} + ); + }); + + it('Should detect no whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-selector-delimiter'], + 'a,b{top:0} a, b{left:0}', + {'space-after-selector-delimiter': ''} + ); + }); + + it('Should detect whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-selector-delimiter'], + 'a, b {top:0} b,a{left:0}', + {'space-after-selector-delimiter': ' '} + ); + }); + + it('Should detect no whitespace (3 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-selector-delimiter'], + 'a, b{top:0} b,c{left:0} c,d{right:0}', + {'space-after-selector-delimiter': ''} + ); + }); + + it('Should detect whitespace (3 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-after-selector-delimiter'], + 'a,b{top:0} b, c{left:0} c, sd{right:0}', + {'space-after-selector-delimiter': ' '} + ); + }); + }); +}); diff --git a/test/options/space-after-selector-delimiter/lint/test.js b/test/options/space-after-selector-delimiter/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-after-selector-delimiter/process/css/test-2.expected.css b/test/options/space-after-selector-delimiter/process/css/test-2.expected.css new file mode 100644 index 00000000..92d1ece8 --- /dev/null +++ b/test/options/space-after-selector-delimiter/process/css/test-2.expected.css @@ -0,0 +1,5 @@ +a, b { color: red } +a, b { color: red } +a , b { color: red } +a, b { color: red } +a+b, c>d, e{ color: red } \ No newline at end of file diff --git a/test/options/space-after-selector-delimiter/process/css/test-3.expected.css b/test/options/space-after-selector-delimiter/process/css/test-3.expected.css new file mode 100644 index 00000000..6a198487 --- /dev/null +++ b/test/options/space-after-selector-delimiter/process/css/test-3.expected.css @@ -0,0 +1,11 @@ +a, + b { color: red } +a, + b { color: red } +a , + b { color: red } +a, + b { color: red } +a+b, + c>d, + e{ color: red } \ No newline at end of file diff --git a/test/options/space-after-selector-delimiter/process/css/test.css b/test/options/space-after-selector-delimiter/process/css/test.css new file mode 100644 index 00000000..cd8e7d77 --- /dev/null +++ b/test/options/space-after-selector-delimiter/process/css/test.css @@ -0,0 +1,6 @@ +a,b { color: red } +a, b { color: red } +a ,b { color: red } +a, +b { color: red } +a+b,c>d,e{ color: red } \ No newline at end of file diff --git a/test/options/space-after-selector-delimiter/process/css/test.expected.css b/test/options/space-after-selector-delimiter/process/css/test.expected.css new file mode 100644 index 00000000..b0d4fd20 --- /dev/null +++ b/test/options/space-after-selector-delimiter/process/css/test.expected.css @@ -0,0 +1,5 @@ +a,b { color: red } +a,b { color: red } +a ,b { color: red } +a,b { color: red } +a+b,c>d,e{ color: red } \ No newline at end of file diff --git a/test/options/space-after-selector-delimiter/process/sass/issue-238.expected.sass b/test/options/space-after-selector-delimiter/process/sass/issue-238.expected.sass new file mode 100644 index 00000000..18571608 --- /dev/null +++ b/test/options/space-after-selector-delimiter/process/sass/issue-238.expected.sass @@ -0,0 +1,5 @@ +html, +body, +p + background: #fff + color: #000 diff --git a/test/options/space-after-selector-delimiter/process/sass/issue-238.sass b/test/options/space-after-selector-delimiter/process/sass/issue-238.sass new file mode 100644 index 00000000..5fc2e131 --- /dev/null +++ b/test/options/space-after-selector-delimiter/process/sass/issue-238.sass @@ -0,0 +1,4 @@ +html, +body, p + background: #fff + color: #000 diff --git a/test/options/space-after-selector-delimiter/process/test.js b/test/options/space-after-selector-delimiter/process/test.js new file mode 100644 index 00000000..4875c8a8 --- /dev/null +++ b/test/options/space-after-selector-delimiter/process/test.js @@ -0,0 +1,42 @@ +let Test = require('../../option_test'); + +describe('Option `space-after-selector-delimiter`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-after-selector-delimiter': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-after-selector-delimiter': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-after-selector-delimiter': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space after selector delimiter', function() { + let test = new Test(this, {'space-after-selector-delimiter': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space after selector delimiter', function() { + let test = new Test(this, {'space-after-selector-delimiter': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space after selector delimiter', function() { + let test = new Test(this, {'space-after-selector-delimiter': '\n '}); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + }); + + describe('sass', function() { + it('Issue 238', function() { + let test = new Test(this, {'space-after-selector-delimiter': '\n'}); + return test.shouldBeEqual('issue-238.sass', 'issue-238.expected.sass'); + }); + }); +}); diff --git a/test/options/space-before-closing-brace/detect/test.js b/test/options/space-before-closing-brace/detect/test.js new file mode 100644 index 00000000..abbed180 --- /dev/null +++ b/test/options/space-before-closing-brace/detect/test.js @@ -0,0 +1,50 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-closing-brace`, detect', function() { + describe('css', function() { + it('Should detect no whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-closing-brace'], + 'a{top:0}', + {'space-before-closing-brace': ''} + ); + }); + + it('Should detect no whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-closing-brace'], + 'a{top:0} b { color: tomato; }', + {'space-before-closing-brace': ''} + ); + }); + + it('Should detect whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-closing-brace'], + 'a { top:0 }', + {'space-before-closing-brace': ' '} + ); + }); + + it('Should detect whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-closing-brace'], + 'a { top:0 } b{color:tomato;}', + {'space-before-closing-brace': ' '} + ); + }); + + it('Should detect whitespace (mix with block indent)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-closing-brace', 'block-indent'], + 'a {\n top:0\n }\nb{\n color:tomato;\n }', + {'block-indent': ' ', 'space-before-closing-brace': '\n '} + ); + }); + }); +}); diff --git a/test/options/space-before-closing-brace/lint/test.js b/test/options/space-before-closing-brace/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-before-closing-brace/process/css/test-2.expected.css b/test/options/space-before-closing-brace/process/css/test-2.expected.css new file mode 100644 index 00000000..86ba2673 --- /dev/null +++ b/test/options/space-before-closing-brace/process/css/test-2.expected.css @@ -0,0 +1,5 @@ +a{top: 0 } +a {top: 0 } + +@media print{a{top: 0 } } +@media print { a { top: 0 } } diff --git a/test/options/space-before-closing-brace/process/css/test-3.expected.css b/test/options/space-before-closing-brace/process/css/test-3.expected.css new file mode 100644 index 00000000..9868eaa7 --- /dev/null +++ b/test/options/space-before-closing-brace/process/css/test-3.expected.css @@ -0,0 +1,11 @@ +a{top: 0 + } +a {top: 0 + } + +@media print{a{top: 0 + } + } +@media print { a { top: 0 + } + } diff --git a/test/options/space-before-closing-brace/process/css/test.css b/test/options/space-before-closing-brace/process/css/test.css new file mode 100644 index 00000000..2c0a59ca --- /dev/null +++ b/test/options/space-before-closing-brace/process/css/test.css @@ -0,0 +1,5 @@ +a{top: 0} +a {top: 0 } + +@media print{a{top: 0}} +@media print { a { top: 0 } } diff --git a/test/options/space-before-closing-brace/process/css/test.expected.css b/test/options/space-before-closing-brace/process/css/test.expected.css new file mode 100644 index 00000000..9a2f6335 --- /dev/null +++ b/test/options/space-before-closing-brace/process/css/test.expected.css @@ -0,0 +1,5 @@ +a{top: 0} +a {top: 0} + +@media print{a{top: 0}} +@media print { a { top: 0}} diff --git a/test/options/space-before-closing-brace/process/test.js b/test/options/space-before-closing-brace/process/test.js new file mode 100644 index 00000000..1bc22dbf --- /dev/null +++ b/test/options/space-before-closing-brace/process/test.js @@ -0,0 +1,36 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-closing-brace`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-before-closing-brace': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-before-closing-brace': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-before-closing-brace': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space before }', function() { + let test = new Test(this, {'space-before-closing-brace': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space before }', function() { + let test = new Test(this, {'space-before-closing-brace': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space before }', function() { + let test = new Test(this, {'space-before-closing-brace': '\n '}); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + }); +}); + diff --git a/test/options/space-before-colon/detect/test.js b/test/options/space-before-colon/detect/test.js new file mode 100644 index 00000000..7189a60a --- /dev/null +++ b/test/options/space-before-colon/detect/test.js @@ -0,0 +1,43 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-colon`, detect', function() { + describe('css', function() { + it('Should detect no whitespaces', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-colon'], + 'a { color:red }', + {'space-before-colon': ''} + ); + }); + + it('Should detect no space from two variants', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-colon'], + 'a { color: red; color :red }', + {'space-before-colon': ''} + ); + }); + + it('Should detect no whitespaces along three variants', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-colon'], + 'a { color: red; background :red } b { width:10px }', + {'space-before-colon': ''} + ); + }); + + it('Should detect space', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-colon'], + 'a { color : red; background :red } b { width:10px }', + {'space-before-colon': ' '} + ); + }); + }); +}); + + diff --git a/test/options/space-before-colon/lint/test.js b/test/options/space-before-colon/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-before-colon/process/css/test-2.expected.css b/test/options/space-before-colon/process/css/test-2.expected.css new file mode 100644 index 00000000..8491cff8 --- /dev/null +++ b/test/options/space-before-colon/process/css/test-2.expected.css @@ -0,0 +1,5 @@ +a { color : red } +a{color :red} +a {color : red} +a {color : /* foo */ red } +a {color /* bar */ : red } diff --git a/test/options/space-before-colon/process/css/test-3.expected.css b/test/options/space-before-colon/process/css/test-3.expected.css new file mode 100644 index 00000000..2d951a78 --- /dev/null +++ b/test/options/space-before-colon/process/css/test-3.expected.css @@ -0,0 +1,10 @@ +a { color + : red } +a{color + :red} +a {color + : red} +a {color + : /* foo */ red } +a {color /* bar */ + : red } diff --git a/test/options/space-before-colon/process/css/test.css b/test/options/space-before-colon/process/css/test.css new file mode 100644 index 00000000..637656fb --- /dev/null +++ b/test/options/space-before-colon/process/css/test.css @@ -0,0 +1,5 @@ +a { color: red } +a{color:red} +a {color : red} +a {color : /* foo */ red } +a {color /* bar */ : red } diff --git a/test/options/space-before-colon/process/css/test.expected.css b/test/options/space-before-colon/process/css/test.expected.css new file mode 100644 index 00000000..9ffc1c4b --- /dev/null +++ b/test/options/space-before-colon/process/css/test.expected.css @@ -0,0 +1,5 @@ +a { color: red } +a{color:red} +a {color: red} +a {color: /* foo */ red } +a {color /* bar */: red } diff --git a/test/options/space-before-colon/process/sass/test.expected.sass b/test/options/space-before-colon/process/sass/test.expected.sass new file mode 100644 index 00000000..a61746f7 --- /dev/null +++ b/test/options/space-before-colon/process/sass/test.expected.sass @@ -0,0 +1,3 @@ +a + color : tomato + top : 0 diff --git a/test/options/space-before-colon/process/sass/test.sass b/test/options/space-before-colon/process/sass/test.sass new file mode 100644 index 00000000..f38512b5 --- /dev/null +++ b/test/options/space-before-colon/process/sass/test.sass @@ -0,0 +1,3 @@ +a + color: tomato + top: 0 diff --git a/test/options/space-before-colon/process/sass/test2.sass b/test/options/space-before-colon/process/sass/test2.sass new file mode 100644 index 00000000..3a5df0b2 --- /dev/null +++ b/test/options/space-before-colon/process/sass/test2.sass @@ -0,0 +1,3 @@ +a + :color tomato + :top 0 diff --git a/test/options/space-before-colon/process/test.js b/test/options/space-before-colon/process/test.js new file mode 100644 index 00000000..c3b860f2 --- /dev/null +++ b/test/options/space-before-colon/process/test.js @@ -0,0 +1,48 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-colon`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-before-colon': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-before-colon': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-before-colon': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space before colon', function() { + let test = new Test(this, {'space-before-colon': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space before colon', function() { + let test = new Test(this, {'space-before-colon': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space before colon', function() { + let test = new Test(this, {'space-before-colon': '\n '}); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + }); + + describe('sass', function() { + it('Should correct space', function() { + let test = new Test(this, {'space-before-colon': 1}); + return test.shouldBeEqual('test.sass', 'test.expected.sass'); + }); + + it('Should not correct space', function() { + let test = new Test(this, {'space-before-colon': 1}); + return test.shouldBeEqual('test2.sass'); + }); + }); +}); + diff --git a/test/options/space-before-combinator/detect/test.js b/test/options/space-before-combinator/detect/test.js new file mode 100644 index 00000000..e24e9a72 --- /dev/null +++ b/test/options/space-before-combinator/detect/test.js @@ -0,0 +1,61 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-combinator`, detect', function() { + describe('css', function() { + it('Should detect no whitespaces before combinator', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-combinator'], + 'a+b { color:red }', + {'space-before-combinator': ''} + ); + }); + + it('Should detect a space before combinator', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-combinator'], + 'a + b { color:red }', + {'space-before-combinator': ' '} + ); + }); + + it('Should detect a space before combinator in long selector', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-combinator'], + 'a + b ~ c>d { color:red }', + {'space-before-combinator': ' '} + ); + }); + + it('Should detect a space before combinator in long selector, test 2', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-combinator'], + 'a>b + c + d { color:red }', + {'space-before-combinator': ' '} + ); + }); + + it('Should detect no whitespaces before combinator in long selector', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-combinator'], + 'a+b ~ c+d { color:red }', + {'space-before-combinator': ''} + ); + }); + + it('Shouldn’t detect whitespaces before combinator in selector without combinators', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-combinator'], + 'a { color:red }', + {} + ); + }); + }); +}); + + diff --git a/test/options/space-before-combinator/lint/test.js b/test/options/space-before-combinator/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-before-combinator/process/css/test-2.expected.css b/test/options/space-before-combinator/process/css/test-2.expected.css new file mode 100644 index 00000000..d1320b14 --- /dev/null +++ b/test/options/space-before-combinator/process/css/test-2.expected.css @@ -0,0 +1,10 @@ +a >b { color: red } +a > b { color: red } +a >b { color: red } +a +b { color: red } +a + b { color: red } +a +b { color: red } +a ~b { color: red } +a ~ b { color: red } +a ~b { color: red } +a ~b + c >d { color: red } diff --git a/test/options/space-before-combinator/process/css/test-3.expected.css b/test/options/space-before-combinator/process/css/test-3.expected.css new file mode 100644 index 00000000..2a7f3179 --- /dev/null +++ b/test/options/space-before-combinator/process/css/test-3.expected.css @@ -0,0 +1,22 @@ +a + >b { color: red } +a + > b { color: red } +a + >b { color: red } +a + +b { color: red } +a + + b { color: red } +a + +b { color: red } +a + ~b { color: red } +a + ~ b { color: red } +a + ~b { color: red } +a + ~b + + c + >d { color: red } diff --git a/test/options/space-before-combinator/process/css/test.css b/test/options/space-before-combinator/process/css/test.css new file mode 100644 index 00000000..4ad8d86e --- /dev/null +++ b/test/options/space-before-combinator/process/css/test.css @@ -0,0 +1,10 @@ +a>b { color: red } +a> b { color: red } +a >b { color: red } +a+b { color: red } +a+ b { color: red } +a +b { color: red } +a~b { color: red } +a~ b { color: red } +a ~b { color: red } +a ~b+ c>d { color: red } diff --git a/test/options/space-before-combinator/process/css/test.expected.css b/test/options/space-before-combinator/process/css/test.expected.css new file mode 100644 index 00000000..920bd510 --- /dev/null +++ b/test/options/space-before-combinator/process/css/test.expected.css @@ -0,0 +1,10 @@ +a>b { color: red } +a> b { color: red } +a>b { color: red } +a+b { color: red } +a+ b { color: red } +a+b { color: red } +a~b { color: red } +a~ b { color: red } +a~b { color: red } +a~b+ c>d { color: red } diff --git a/test/options/space-before-combinator/process/scss/test.expected.scss b/test/options/space-before-combinator/process/scss/test.expected.scss new file mode 100644 index 00000000..d325dfaf --- /dev/null +++ b/test/options/space-before-combinator/process/scss/test.expected.scss @@ -0,0 +1,15 @@ +a >b { + color: red; + + & >c { + color: red; + } +} + +a { + color: red; + + >c { + color: red; + } +} diff --git a/test/options/space-before-combinator/process/scss/test.scss b/test/options/space-before-combinator/process/scss/test.scss new file mode 100644 index 00000000..f7d1894b --- /dev/null +++ b/test/options/space-before-combinator/process/scss/test.scss @@ -0,0 +1,15 @@ +a>b { + color: red; + + &>c { + color: red; + } +} + +a { + color: red; + + >c { + color: red; + } +} diff --git a/test/options/space-before-combinator/process/test.js b/test/options/space-before-combinator/process/test.js new file mode 100644 index 00000000..523f3b60 --- /dev/null +++ b/test/options/space-before-combinator/process/test.js @@ -0,0 +1,42 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-combinator`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-before-combinator': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-before-combinator': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-before-combinator': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space before combinator', function() { + let test = new Test(this, {'space-before-combinator': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space before combinator', function() { + let test = new Test(this, {'space-before-combinator': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space before combinator', function() { + let test = new Test(this, {'space-before-combinator': '\n '}); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + }); + + describe('scss', function() { + it('Should not touch leading combinators', function() { + let test = new Test(this, {'space-before-combinator': ' '}); + return test.shouldBeEqual('test.scss', 'test.expected.scss'); + }); + }); +}); diff --git a/test/options/space-before-opening-brace/detect/test.js b/test/options/space-before-opening-brace/detect/test.js new file mode 100644 index 00000000..b75bfae9 --- /dev/null +++ b/test/options/space-before-opening-brace/detect/test.js @@ -0,0 +1,59 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-opening-brace`, detect', function() { + describe('css', function() { + it('Should detect no whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-opening-brace'], + 'a{top:0}', + {'space-before-opening-brace': ''} + ); + }); + + it('Should detect whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-opening-brace'], + 'a \n {top:0}', + {'space-before-opening-brace': ' \n '} + ); + }); + + it('Should detect no whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-opening-brace'], + 'a{top:0} b {left:0}', + {'space-before-opening-brace': ''} + ); + }); + + it('Should detect whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-opening-brace'], + 'a {top:0} b{left:0}', + {'space-before-opening-brace': ' '} + ); + }); + + it('Should detect no whitespace (3 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-opening-brace'], + 'a {top:0} b{left:0} c{right:0}', + {'space-before-opening-brace': ''} + ); + }); + + it('Should detect whitespace (3 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-opening-brace'], + 'a{top:0} b {left:0} c {right:0}', + {'space-before-opening-brace': ' '} + ); + }); + }); +}); diff --git a/test/options/space-before-opening-brace/lint/test.js b/test/options/space-before-opening-brace/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-before-opening-brace/process/css/issue-232.css b/test/options/space-before-opening-brace/process/css/issue-232.css new file mode 100644 index 00000000..bc99e743 --- /dev/null +++ b/test/options/space-before-opening-brace/process/css/issue-232.css @@ -0,0 +1,13 @@ +@font-face +{ + font-family: Panda; + src: url(panda.ttf); +} + +@media screen and (max-width: 400px) +{ + @-ms-viewport + { + width: 320px; + } +} diff --git a/test/options/space-before-opening-brace/process/css/issue-232.expected.css b/test/options/space-before-opening-brace/process/css/issue-232.expected.css new file mode 100644 index 00000000..04d76f38 --- /dev/null +++ b/test/options/space-before-opening-brace/process/css/issue-232.expected.css @@ -0,0 +1,10 @@ +@font-face { + font-family: Panda; + src: url(panda.ttf); +} + +@media screen and (max-width: 400px) { + @-ms-viewport { + width: 320px; + } +} diff --git a/test/options/space-before-opening-brace/process/css/test-2.expected.css b/test/options/space-before-opening-brace/process/css/test-2.expected.css new file mode 100644 index 00000000..68a688a2 --- /dev/null +++ b/test/options/space-before-opening-brace/process/css/test-2.expected.css @@ -0,0 +1,5 @@ +a {top: 0} +a {top: 0} + +@media print {a {top: 0}} +@media print { a { top: 0 } } diff --git a/test/options/space-before-opening-brace/process/css/test-3.expected.css b/test/options/space-before-opening-brace/process/css/test-3.expected.css new file mode 100644 index 00000000..8efc4e9d --- /dev/null +++ b/test/options/space-before-opening-brace/process/css/test-3.expected.css @@ -0,0 +1,11 @@ +a + {top: 0} +a + {top: 0} + +@media print + {a + {top: 0}} +@media print + { a + { top: 0 } } diff --git a/test/options/space-before-opening-brace/process/css/test.css b/test/options/space-before-opening-brace/process/css/test.css new file mode 100644 index 00000000..0f8fadcc --- /dev/null +++ b/test/options/space-before-opening-brace/process/css/test.css @@ -0,0 +1,5 @@ +a{top: 0} +a {top: 0} + +@media print{a{top: 0}} +@media print { a { top: 0 } } diff --git a/test/options/space-before-opening-brace/process/css/test.expected.css b/test/options/space-before-opening-brace/process/css/test.expected.css new file mode 100644 index 00000000..32014ddb --- /dev/null +++ b/test/options/space-before-opening-brace/process/css/test.expected.css @@ -0,0 +1,5 @@ +a{top: 0} +a{top: 0} + +@media print{a{top: 0}} +@media print{ a{ top: 0 } } diff --git a/test/options/space-before-opening-brace/process/scss/issue-231.expected.scss b/test/options/space-before-opening-brace/process/scss/issue-231.expected.scss new file mode 100644 index 00000000..917eb73a --- /dev/null +++ b/test/options/space-before-opening-brace/process/scss/issue-231.expected.scss @@ -0,0 +1,8 @@ +li { + float: left; + @include respond-to(mobile) { + float: none; + width: $width; + $width: 100%; + } +} diff --git a/test/options/space-before-opening-brace/process/scss/issue-231.scss b/test/options/space-before-opening-brace/process/scss/issue-231.scss new file mode 100644 index 00000000..f0abf87d --- /dev/null +++ b/test/options/space-before-opening-brace/process/scss/issue-231.scss @@ -0,0 +1,8 @@ +li{ + float: left; + @include respond-to(mobile){ + float: none; + width: $width; + $width: 100%; + } +} diff --git a/test/options/space-before-opening-brace/process/scss/issue-319.expected.scss b/test/options/space-before-opening-brace/process/scss/issue-319.expected.scss new file mode 100644 index 00000000..4b0777bf --- /dev/null +++ b/test/options/space-before-opening-brace/process/scss/issue-319.expected.scss @@ -0,0 +1,9 @@ +@import '../../helpers/__helpers'; + +.funky { + font: { + family: fantasy; + size: 30em; + weight: bold; + } +} diff --git a/test/options/space-before-opening-brace/process/scss/issue-319.scss b/test/options/space-before-opening-brace/process/scss/issue-319.scss new file mode 100644 index 00000000..068141cc --- /dev/null +++ b/test/options/space-before-opening-brace/process/scss/issue-319.scss @@ -0,0 +1,9 @@ +@import '../../helpers/__helpers'; + +.funky{ + font:{ + family: fantasy; + size: 30em; + weight: bold; + } +} diff --git a/test/options/space-before-opening-brace/process/test.js b/test/options/space-before-opening-brace/process/test.js new file mode 100644 index 00000000..dbf0de74 --- /dev/null +++ b/test/options/space-before-opening-brace/process/test.js @@ -0,0 +1,52 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-opening-brace`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-before-opening-brace': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-before-opening-brace': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-before-opening-brace': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space before {', function() { + let test = new Test(this, {'space-before-opening-brace': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space before {', function() { + let test = new Test(this, {'space-before-opening-brace': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space before {', function() { + let test = new Test(this, {'space-before-opening-brace': '\n '}); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + + it('Issue 232', function() { + let test = new Test(this, {'space-before-opening-brace': 1}); + return test.shouldBeEqual('issue-232.css', 'issue-232.expected.css'); + }); + }); + + describe('scss', function() { + it('Issue 231', function() { + let test = new Test(this, {'space-before-opening-brace': 1}); + return test.shouldBeEqual('issue-231.scss', 'issue-231.expected.scss'); + }); + + it('Issue 319', function() { + let test = new Test(this, {'space-before-opening-brace': 1}); + return test.shouldBeEqual('issue-319.scss', 'issue-319.expected.scss'); + }); + }); +}); diff --git a/test/options/space-before-selector-delimiter/detect/test.js b/test/options/space-before-selector-delimiter/detect/test.js new file mode 100644 index 00000000..72f67296 --- /dev/null +++ b/test/options/space-before-selector-delimiter/detect/test.js @@ -0,0 +1,59 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-selector-delimiter`, detect', function() { + describe('css', function() { + it('Should detect no whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-selector-delimiter'], + 'a,b{top:0}', + {'space-before-selector-delimiter': ''} + ); + }); + + it('Should detect whitespace', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-selector-delimiter'], + 'a \n ,b {top:0}', + {'space-before-selector-delimiter': ' \n '} + ); + }); + + it('Should detect no whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-selector-delimiter'], + 'a,b{top:0} a ,b{left:0}', + {'space-before-selector-delimiter': ''} + ); + }); + + it('Should detect whitespace (2 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-selector-delimiter'], + 'a ,b {top:0} b,a{left:0}', + {'space-before-selector-delimiter': ' '} + ); + }); + + it('Should detect no whitespace (3 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-selector-delimiter'], + 'a ,b{top:0} b,c{left:0} c,d{right:0}', + {'space-before-selector-delimiter': ''} + ); + }); + + it('Should detect whitespace (3 blocks)', function() { + let test = new Test(this); + test.shouldDetect( + ['space-before-selector-delimiter'], + 'a,b{top:0} b ,c{left:0} c ,d{right:0}', + {'space-before-selector-delimiter': ' '} + ); + }); + }); +}); diff --git a/test/options/space-before-selector-delimiter/lint/test.js b/test/options/space-before-selector-delimiter/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-before-selector-delimiter/process/css/test-2.expected.css b/test/options/space-before-selector-delimiter/process/css/test-2.expected.css new file mode 100644 index 00000000..90b232e1 --- /dev/null +++ b/test/options/space-before-selector-delimiter/process/css/test-2.expected.css @@ -0,0 +1,6 @@ +a ,b { color: red } +a , b { color: red } +a ,b { color: red } +a , +b { color: red } +a+b ,c>d ,e{ color: red } \ No newline at end of file diff --git a/test/options/space-before-selector-delimiter/process/css/test-3.expected.css b/test/options/space-before-selector-delimiter/process/css/test-3.expected.css new file mode 100644 index 00000000..1b454c84 --- /dev/null +++ b/test/options/space-before-selector-delimiter/process/css/test-3.expected.css @@ -0,0 +1,12 @@ +a + ,b { color: red } +a + , b { color: red } +a + ,b { color: red } +a + , +b { color: red } +a+b + ,c>d + ,e{ color: red } \ No newline at end of file diff --git a/test/options/space-before-selector-delimiter/process/css/test.css b/test/options/space-before-selector-delimiter/process/css/test.css new file mode 100644 index 00000000..cd8e7d77 --- /dev/null +++ b/test/options/space-before-selector-delimiter/process/css/test.css @@ -0,0 +1,6 @@ +a,b { color: red } +a, b { color: red } +a ,b { color: red } +a, +b { color: red } +a+b,c>d,e{ color: red } \ No newline at end of file diff --git a/test/options/space-before-selector-delimiter/process/css/test.expected.css b/test/options/space-before-selector-delimiter/process/css/test.expected.css new file mode 100644 index 00000000..3c566c55 --- /dev/null +++ b/test/options/space-before-selector-delimiter/process/css/test.expected.css @@ -0,0 +1,6 @@ +a,b { color: red } +a, b { color: red } +a,b { color: red } +a, +b { color: red } +a+b,c>d,e{ color: red } \ No newline at end of file diff --git a/test/options/space-before-selector-delimiter/process/test.js b/test/options/space-before-selector-delimiter/process/test.js new file mode 100644 index 00000000..828e820f --- /dev/null +++ b/test/options/space-before-selector-delimiter/process/test.js @@ -0,0 +1,35 @@ +let Test = require('../../option_test'); + +describe('Option `space-before-selector-delimiter`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-before-selector-delimiter': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-before-selector-delimiter': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-before-selector-delimiter': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space before selector delimiter', function() { + let test = new Test(this, {'space-before-selector-delimiter': 0}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space before selector delimiter', function() { + let test = new Test(this, {'space-before-selector-delimiter': ' '}); + return test.shouldBeEqual('test.css', 'test-2.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space before selector delimiter', function() { + let test = new Test(this, {'space-before-selector-delimiter': '\n '}); + return test.shouldBeEqual('test.css', 'test-3.expected.css'); + }); + }); +}); diff --git a/test/options/space-between-declarations/detect/test.js b/test/options/space-between-declarations/detect/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-between-declarations/lint/test.js b/test/options/space-between-declarations/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/space-between-declarations/process/css/comments.css b/test/options/space-between-declarations/process/css/comments.css new file mode 100644 index 00000000..0d8b9310 --- /dev/null +++ b/test/options/space-between-declarations/process/css/comments.css @@ -0,0 +1,8 @@ +a { + color: tomato; + /* comment */ + top: 0; /* comment */ + left: 10px; + right: 0; + /* comment */ + } diff --git a/test/options/space-between-declarations/process/css/comments.expected.css b/test/options/space-between-declarations/process/css/comments.expected.css new file mode 100644 index 00000000..5903c202 --- /dev/null +++ b/test/options/space-between-declarations/process/css/comments.expected.css @@ -0,0 +1,6 @@ +a { + color: tomato; + /* comment */ + top: 0; /* comment */ left: 10px; right: 0; + /* comment */ + } diff --git a/test/options/space-between-declarations/process/css/integer-value.css b/test/options/space-between-declarations/process/css/integer-value.css new file mode 100644 index 00000000..de2c6491 --- /dev/null +++ b/test/options/space-between-declarations/process/css/integer-value.css @@ -0,0 +1,6 @@ +a { + color: tomato; + + top: 0; + left: 10px + } diff --git a/test/options/space-between-declarations/process/css/integer-value.expected.css b/test/options/space-between-declarations/process/css/integer-value.expected.css new file mode 100644 index 00000000..5d120170 --- /dev/null +++ b/test/options/space-between-declarations/process/css/integer-value.expected.css @@ -0,0 +1,3 @@ +a { + color: tomato;top: 0;left: 10px + } diff --git a/test/options/space-between-declarations/process/css/issue-239.css b/test/options/space-between-declarations/process/css/issue-239.css new file mode 100644 index 00000000..ccf6ba7c --- /dev/null +++ b/test/options/space-between-declarations/process/css/issue-239.css @@ -0,0 +1,2 @@ +html{ + color:#444;font-family:proxima-nova;font-size:62.5%;font-weight:300;line-height:1.5;} diff --git a/test/options/space-between-declarations/process/css/issue-239.expected.css b/test/options/space-between-declarations/process/css/issue-239.expected.css new file mode 100644 index 00000000..ab145f70 --- /dev/null +++ b/test/options/space-between-declarations/process/css/issue-239.expected.css @@ -0,0 +1,6 @@ +html{ + color:#444; + font-family:proxima-nova; + font-size:62.5%; + font-weight:300; + line-height:1.5;} diff --git a/test/options/space-between-declarations/process/css/issue-378.css b/test/options/space-between-declarations/process/css/issue-378.css new file mode 100644 index 00000000..4c8dbfb6 --- /dev/null +++ b/test/options/space-between-declarations/process/css/issue-378.css @@ -0,0 +1,3 @@ +@media (min-width: 768px) { + body { background: red; } +} diff --git a/test/options/space-between-declarations/process/css/space-newline-value.css b/test/options/space-between-declarations/process/css/space-newline-value.css new file mode 100644 index 00000000..ff86611c --- /dev/null +++ b/test/options/space-between-declarations/process/css/space-newline-value.css @@ -0,0 +1,4 @@ +a { + color: tomato;top: 0; left: 10px; + right: 0; width: 100% \9; +} diff --git a/test/options/space-between-declarations/process/css/space-newline-value.expected.css b/test/options/space-between-declarations/process/css/space-newline-value.expected.css new file mode 100644 index 00000000..17c237ac --- /dev/null +++ b/test/options/space-between-declarations/process/css/space-newline-value.expected.css @@ -0,0 +1,7 @@ +a { + color: tomato; + top: 0; + left: 10px; + right: 0; + width: 100% \9; +} diff --git a/test/options/space-between-declarations/process/css/space-value.css b/test/options/space-between-declarations/process/css/space-value.css new file mode 100644 index 00000000..de2c6491 --- /dev/null +++ b/test/options/space-between-declarations/process/css/space-value.css @@ -0,0 +1,6 @@ +a { + color: tomato; + + top: 0; + left: 10px + } diff --git a/test/options/space-between-declarations/process/css/space-value.expected.css b/test/options/space-between-declarations/process/css/space-value.expected.css new file mode 100644 index 00000000..6795d9c4 --- /dev/null +++ b/test/options/space-between-declarations/process/css/space-value.expected.css @@ -0,0 +1,3 @@ +a { + color: tomato; top: 0; left: 10px + } diff --git a/test/options/space-between-declarations/process/css/test.css b/test/options/space-between-declarations/process/css/test.css new file mode 100644 index 00000000..7a75e51d --- /dev/null +++ b/test/options/space-between-declarations/process/css/test.css @@ -0,0 +1,2 @@ +a { + animal: panda;color: tomato; top: 0; } diff --git a/test/options/space-between-declarations/process/test.js b/test/options/space-between-declarations/process/test.js new file mode 100644 index 00000000..f230d06c --- /dev/null +++ b/test/options/space-between-declarations/process/test.js @@ -0,0 +1,50 @@ +let Test = require('../../option_test'); + +describe('Option `space-between-declarations`, process', function() { + describe('css', function() { + it('Array value => should not change anything', function() { + let test = new Test(this, {'space-between-declarations': ['', ' ']}); + return test.shouldBeEqual('test.css'); + }); + + it('Invalid string value => should not change anything', function() { + let test = new Test(this, {'space-between-declarations': ' nani '}); + return test.shouldBeEqual('test.css'); + }); + + it('Float number value => should not change anything', function() { + let test = new Test(this, {'space-between-declarations': 3.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Integer value => should set proper space after declaration', function() { + let test = new Test(this, {'space-between-declarations': 0}); + return test.shouldBeEqual('integer-value.css', 'integer-value.expected.css'); + }); + + it('Valid string value (spaces only) => should set proper space after declaration', function() { + let test = new Test(this, {'space-between-declarations': ' '}); + return test.shouldBeEqual('space-value.css', 'space-value.expected.css'); + }); + + it('Valid string value (spaces and newlines) => should set proper space after declaration', function() { + let test = new Test(this, {'space-between-declarations': '\n '}); + return test.shouldBeEqual('space-newline-value.css', 'space-newline-value.expected.css'); + }); + + it('Should leave comments as is', function() { + let test = new Test(this, {'space-between-declarations': 1}); + return test.shouldBeEqual('comments.css', 'comments.expected.css'); + }); + + it('Issue 239', function() { + let test = new Test(this, {'space-between-declarations': '\n '}); + return test.shouldBeEqual('issue-239.css', 'issue-239.expected.css'); + }); + + it('Issue 378', function() { + let test = new Test(this, {'space-between-declarations': '\n'}); + return test.shouldBeEqual('issue-378.css'); + }); + }); +}); diff --git a/test/options/stick-brace.js b/test/options/stick-brace.js deleted file mode 100644 index d167f8f7..00000000 --- a/test/options/stick-brace.js +++ /dev/null @@ -1,104 +0,0 @@ -var assert = require('assert'); - -describe('options/stick-brace', function() { - it('Boolean value should not change space before brace', function() { - var input = 'a { color: red }'; - this.comb.configure({ 'stick-brace': 'foobar' }); - assert.equal(this.comb.processString(input), input); - }); - - it('Invalid String should not change space before brace', function() { - var input = 'a { color: red }'; - this.comb.configure({ 'stick-brace': 'foobar' }); - assert.equal(this.comb.processString(input), input); - }); - - it('Invalid Number should not change space before brace', function() { - var input = 'a { color: red }'; - this.comb.configure({ 'stick-brace': 3.5 }); - assert.equal(this.comb.processString(input), input); - }); - - it('Valid Number value should set equal space before brace', function() { - this.comb.configure({ 'stick-brace': 0 }); - assert.equal( - this.comb.processString('a {color:red }'), - 'a{color:red }' - ); - }); - - it('Valid String value should set equal space before brace', function() { - this.comb.configure({ 'stick-brace': '\n' }); - assert.equal( - this.comb.processString( - 'a{ color: red }' + - 'a, b /* i */ { color: red; }' + - 'a \t\t \n{color:red\n \n}' + - 'a /* foo */ {color:red ;\n}' + - '@media all { .input__control { color: #000;\n \n }\t}' - ), - 'a\n{ color: red }' + - 'a, b /* i */\n{ color: red; }' + - 'a\n{color:red\n \n}' + - 'a /* foo */\n{color:red ;\n}' + - '@media all\n{ .input__control\n{ color: #000;\n \n }\t}' - ); - }); - it('Empty String value should set no space before brace', function() { - this.comb.configure({ 'stick-brace': '' }); - assert.equal( - this.comb.processString( - 'a{ color: red }' + - 'a, b /* i */ { color: red; }' + - 'a \t\t \n{color:red\n \n}' + - 'a /* foo */ {color:red ;\n}' + - '@media all { .input__control { color: #000;\n \n }\t}' - ), - 'a{ color: red }' + - 'a, b /* i */{ color: red; }' + - 'a{color:red\n \n}' + - 'a /* foo */{color:red ;\n}' + - '@media all{ .input__control{ color: #000;\n \n }\t}' - ); - }); - - it('Should detect the empty stick-brace option', function() { - this.shouldDetect( - ['stick-brace'], - 'a{ color: red }', - { - 'stick-brace': '' - } - ); - }); - - it('Should detect the stick-brace option equal to a single space', function() { - this.shouldDetect( - ['stick-brace'], - 'a {\ncolor: red }', - { - 'stick-brace': ' ' - } - ); - }); - - it('Should detect the stick-brace option equal to a newline with spaces', function() { - this.shouldDetect( - ['stick-brace'], - '.input__control\n { color: #000;\n \n }', - { - 'stick-brace': '\n ' - } - ); - }); - - it('Should detect the stick-brace option equal to a newline when nested in mq', function() { - this.shouldDetect( - ['stick-brace'], - '@media all\n{\n .input__control\n { color: #000;\n \n }\t}', - { - 'stick-brace': '\n' - } - ); - }); -}); diff --git a/test/options/strip-spaces.js b/test/options/strip-spaces.js deleted file mode 100644 index b8f19b36..00000000 --- a/test/options/strip-spaces.js +++ /dev/null @@ -1,113 +0,0 @@ -var assert = require('assert'); - -describe('options/strip-spaces', function() { - it('Invalid value should not trim trailing spaces', function() { - this.comb.configure({ 'strip-spaces': 'foobar' }); - assert.equal( - this.comb.processString('a { color: red } \n'), - 'a { color: red } \n' - ); - }); - it('Boolean true value should trim all trailing spaces', function() { - this.comb.configure({ 'strip-spaces': true }); - assert.equal( - this.comb.processString( - 'a { color: red } \n' + - 'a{color:red}\t /* foobar */\t \n' + - 'a {color:red} \n \n' - ), - 'a { color: red }\n' + - 'a{color:red}\t /* foobar */\n' + - 'a {color:red}\n' - ); - }); - it('Boolean true value should trim trailing spaces at eof', function() { - this.comb.configure({ 'strip-spaces': true }); - assert.equal( - this.comb.processString( - 'a {color:red} ' - ), - 'a {color:red}' - ); - }); - - it('Should detect strip-spaces option set to `true`', function() { - this.shouldDetect( - ['strip-spaces'], - 'a { color: red }', - { - 'strip-spaces': true - } - ); - }); - - it('Should detect strip-spaces option set to `false`', function() { - this.shouldDetect( - ['strip-spaces'], - 'a { color: red } ', - { - 'strip-spaces': false - } - ); - }); - - it('Should detect strip-spaces option set to `true` with newline', function() { - this.shouldDetect( - ['strip-spaces'], - 'a { color: red }\nb { color: blue }', - { - 'strip-spaces': true - } - ); - }); - - it('Should detect strip-spaces option set to `false` with newline', function() { - this.shouldDetect( - ['strip-spaces'], - 'a { color: red } \nb { color: blue }', - { - 'strip-spaces': false - } - ); - }); - - it('Should detect strip-spaces option set to `true` inside a value', function() { - this.shouldDetect( - ['strip-spaces'], - 'a {\n color:\n red }', - { - 'strip-spaces': true - } - ); - }); - - it('Should detect strip-spaces option set to `false` inside a value', function() { - this.shouldDetect( - ['strip-spaces'], - 'a {\n color: \n red }', - { - 'strip-spaces': false - } - ); - }); - - it('Should detect strip-spaces option set to `true` if the only trailing space is the last newline', function() { - this.shouldDetect( - ['strip-spaces'], - 'a { color: red }\n', - { - 'strip-spaces': true - } - ); - }); - - it('Should detect strip-spaces option set to `false` if there is more than one newline at the end', function() { - this.shouldDetect( - ['strip-spaces'], - 'a { color: red }\n\n', - { - 'strip-spaces': false - } - ); - }); -}); diff --git a/test/options/strip-spaces/detect/test.js b/test/options/strip-spaces/detect/test.js new file mode 100644 index 00000000..e121275c --- /dev/null +++ b/test/options/strip-spaces/detect/test.js @@ -0,0 +1,77 @@ +let Test = require('../../option_test'); + +describe('Option `strip-spaces`, detect', function() { + describe('css', function() { + it('Should detect strip-spaces option set to `true`', function() { + let test = new Test(this); + test.shouldDetect( + ['strip-spaces'], + 'a { color: red }', + {'strip-spaces': true} + ); + }); + + it('Should detect strip-spaces option set to `false`', function() { + let test = new Test(this); + test.shouldDetect( + ['strip-spaces'], + 'a { color: red } ', + {'strip-spaces': false} + ); + }); + + it('Should detect strip-spaces option set to `true` with newline', function() { + let test = new Test(this); + test.shouldDetect( + ['strip-spaces'], + 'a { color: red }\nb { color: blue }', + {'strip-spaces': true} + ); + }); + + it('Should detect strip-spaces option set to `false` with newline', function() { + let test = new Test(this); + test.shouldDetect( + ['strip-spaces'], + 'a { color: red } \nb { color: blue }', + {'strip-spaces': false} + ); + }); + + it('Should detect strip-spaces option set to `true` inside a value', function() { + let test = new Test(this); + test.shouldDetect( + ['strip-spaces'], + 'a {\n color:\n red }', + {'strip-spaces': true} + ); + }); + + it('Should detect strip-spaces option set to `false` inside a value', function() { + let test = new Test(this); + test.shouldDetect( + ['strip-spaces'], + 'a {\n color: \n red }', + {'strip-spaces': false} + ); + }); + + it('Should detect strip-spaces option set to `true` if the only trailing space is the last newline', function() { + let test = new Test(this); + test.shouldDetect( + ['strip-spaces'], + 'a { color: red }\n', + {'strip-spaces': true} + ); + }); + + it('Should detect strip-spaces option set to `false` if there is more than one newline at the end', function() { + let test = new Test(this); + test.shouldDetect( + ['strip-spaces'], + 'a { color: red }\n\n', + {'strip-spaces': false} + ); + }); + }); +}); diff --git a/test/options/strip-spaces/lint/test.js b/test/options/strip-spaces/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/strip-spaces/process/test.js b/test/options/strip-spaces/process/test.js new file mode 100644 index 00000000..1c73598c --- /dev/null +++ b/test/options/strip-spaces/process/test.js @@ -0,0 +1,37 @@ +var assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `strip-spaces`, process', function() { + describe('css', function() { + it('Invalid value should not trim trailing spaces', function() { + let test = new Test(this, {'strip-spaces': 'foobar'}); + return test.comb.processString('a { color: red } \n') + .then(function(actual) { + assert.equal(actual, 'a { color: red } \n'); + }); + }); + + it('Boolean true value should trim all trailing spaces', function() { + let test = new Test(this, {'strip-spaces': true}); + return test.comb.processString( + 'a { color: red } \n' + + 'a{color:red}\t /* foobar */\t \n' + + 'a {color:red} \n \n' + ).then(function(actual) { + assert.equal(actual, + 'a { color: red }\n' + + 'a{color:red}\t /* foobar */\n' + + 'a {color:red}\n'); + }); + }); + + it('Boolean true value should trim trailing spaces at eof', function() { + let test = new Test(this, {'strip-spaces': true}); + return test.comb.processString( + 'a {color:red} ' + ).then(function(actual) { + assert.equal(actual, 'a {color:red}'); + }); + }); + }); +}); diff --git a/test/options/tab-size/detect/test.js b/test/options/tab-size/detect/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/tab-size/lint/test.js b/test/options/tab-size/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/tab-size/process/css/test.css b/test/options/tab-size/process/css/test.css new file mode 100644 index 00000000..a5ff8ef6 --- /dev/null +++ b/test/options/tab-size/process/css/test.css @@ -0,0 +1,4 @@ +a { + color: tomato; + top: 0; + } diff --git a/test/options/tab-size/process/css/test.expected.css b/test/options/tab-size/process/css/test.expected.css new file mode 100644 index 00000000..248453a5 --- /dev/null +++ b/test/options/tab-size/process/css/test.expected.css @@ -0,0 +1,4 @@ +a { + color: tomato; + top: 0; + } diff --git a/test/options/tab-size/process/test.js b/test/options/tab-size/process/test.js new file mode 100644 index 00000000..ea4a4650 --- /dev/null +++ b/test/options/tab-size/process/test.js @@ -0,0 +1,20 @@ +let Test = require('../../option_test'); + +describe('Option `tab-size`, process', function() { + describe('css', function() { + it('Test 1: String value => should not change anything', function() { + let test = new Test(this, {'tab-size': ' '}); + return test.shouldBeEqual('test.css'); + }); + + it('Test 2: Float value => should not change anything', function() { + let test = new Test(this, {'tab-size': 4.5}); + return test.shouldBeEqual('test.css'); + }); + + it('Test 3: Integer value => should replace tabs with proper number of spaces', function() { + let test = new Test(this, {'tab-size': 4}); + return test.shouldBeEqual('test.css', 'test.expected.css'); + }); + }); +}); diff --git a/test/options/unitless-zero.js b/test/options/unitless-zero.js deleted file mode 100644 index f47be91d..00000000 --- a/test/options/unitless-zero.js +++ /dev/null @@ -1,103 +0,0 @@ -var assert = require('assert'); - -describe('options/unitless-zero', function() { - it('Should remove units in zero-valued dimensions', function() { - this.comb.configure({ 'unitless-zero': true }); - assert.equal( - this.comb.processString( - 'div { margin: 0em; padding: 0px }' - ), - 'div { margin: 0; padding: 0 }' - ); - assert.equal( - this.comb.processString( - 'div { margin: 0% }' - ), - 'div { margin: 0 }' - ); - }); - - it('Should remove units in zero-valued media-query params', function() { - this.comb.configure({ 'unitless-zero': true }); - assert.equal( - this.comb.processString('@media all and (min-width: 0px) { div { margin: 0em; padding: 0px } }'), - '@media all and (min-width: 0) { div { margin: 0; padding: 0 } }' - ); - }); - - it('Should not remove units (degs) in rotate property', function() { - this.comb.configure({ 'unitless-zero': true }); - assert.equal( - this.comb.processString( - 'div { -webkit-transform: rotate(0deg); }' - ), - 'div { -webkit-transform: rotate(0deg); }' - ); - }); - - it('Should detect unitless zero option', function() { - this.shouldDetect( - ['unitless-zero'], - 'a { width: 0 }', - { - 'unitless-zero': true - } - ); - }); - - it('Should detect zero with unit', function() { - this.shouldDetect( - ['unitless-zero'], - 'a { width: 0px }', - { - 'unitless-zero': false - } - ); - }); - - it('Should detect unitless zero option with multiple values', function() { - this.shouldDetect( - ['unitless-zero'], - 'a { padding: 0px 0 0 }', - { - 'unitless-zero': true - } - ); - }); - - it('Should detect zero with unit and multiple values', function() { - this.shouldDetect( - ['unitless-zero'], - 'a { padding: 0px 0 0em }', - { - 'unitless-zero': false - } - ); - }); - - it('Shouldn’t detect unitless zero option if there is no unit', function() { - this.shouldDetect( - ['unitless-zero'], - 'a { color: red }', - {} - ); - }); - - it('Shouldn’t detect unitless zero option if there is `deg` unit', function() { - this.shouldDetect( - ['unitless-zero'], - 'a { transform: rotate(0deg) }', - {} - ); - }); - - it('Should detect unitless zero option with percents', function() { - this.shouldDetect( - ['unitless-zero'], - 'a { padding: 0% 0 0 }', - { - 'unitless-zero': true - } - ); - }); -}); diff --git a/test/options/unitless-zero/detect/test.js b/test/options/unitless-zero/detect/test.js new file mode 100644 index 00000000..5d407a49 --- /dev/null +++ b/test/options/unitless-zero/detect/test.js @@ -0,0 +1,69 @@ +let Test = require('../../option_test'); + +describe('Option `unitless-zero`, detect', function() { + describe('css', function() { + it('Should detect unitless zero option', function() { + let test = new Test(this); + test.shouldDetect( + ['unitless-zero'], + 'a { width: 0 }', + {'unitless-zero': true} + ); + }); + + it('Should detect zero with unit', function() { + let test = new Test(this); + test.shouldDetect( + ['unitless-zero'], + 'a { width: 0px }', + {'unitless-zero': false} + ); + }); + + it('Should detect unitless zero option with multiple values', function() { + let test = new Test(this); + test.shouldDetect( + ['unitless-zero'], + 'a { padding: 0px 0 0 }', + {'unitless-zero': true} + ); + }); + + it('Should detect zero with unit and multiple values', function() { + let test = new Test(this); + test.shouldDetect( + ['unitless-zero'], + 'a { padding: 0px 0 0em }', + {'unitless-zero': false} + ); + }); + + it('Shouldn’t detect unitless zero option if there is no unit', function() { + let test = new Test(this); + test.shouldDetect( + ['unitless-zero'], + 'a { color: red }', + {} + ); + }); + + it('Shouldn’t detect unitless zero option if there is `deg` unit', function() { + let test = new Test(this); + test.shouldDetect( + ['unitless-zero'], + 'a { transform: rotate(0deg) }', + {} + ); + }); + + it('Should detect unitless zero option with percents', function() { + let test = new Test(this); + test.shouldDetect( + ['unitless-zero'], + 'a { padding: 0% 0 0 }', + {'unitless-zero': true} + ); + }); + }); +}); + diff --git a/test/options/unitless-zero/lint/test.js b/test/options/unitless-zero/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/unitless-zero/process/css/issue-394.css b/test/options/unitless-zero/process/css/issue-394.css new file mode 100644 index 00000000..065b1142 --- /dev/null +++ b/test/options/unitless-zero/process/css/issue-394.css @@ -0,0 +1,6 @@ +.example { + bottom: 0em; + left: 0.5em; + right: 1em; + top: 1.5em; +} diff --git a/test/options/unitless-zero/process/css/issue-394.expected.css b/test/options/unitless-zero/process/css/issue-394.expected.css new file mode 100644 index 00000000..dc9de0f0 --- /dev/null +++ b/test/options/unitless-zero/process/css/issue-394.expected.css @@ -0,0 +1,6 @@ +.example { + bottom: 0; + left: 0.5em; + right: 1em; + top: 1.5em; +} diff --git a/test/options/unitless-zero/process/less/issue-389.less b/test/options/unitless-zero/process/less/issue-389.less new file mode 100644 index 00000000..f0626d68 --- /dev/null +++ b/test/options/unitless-zero/process/less/issue-389.less @@ -0,0 +1,4 @@ +.test +{ + width: 100%; +} diff --git a/test/options/unitless-zero/process/test.js b/test/options/unitless-zero/process/test.js new file mode 100644 index 00000000..1f2057aa --- /dev/null +++ b/test/options/unitless-zero/process/test.js @@ -0,0 +1,54 @@ +var assert = require('assert'); +let Test = require('../../option_test'); + +describe('Option `unitless-zero`, process', function() { + describe('css', function() { + it('Should remove units in zero-valued dimensions', function() { + let test = new Test(this, {'unitless-zero': true}); + return test.comb.processString( + 'div { margin: 0em; padding: 0px }' + ).then(function(actual) { + assert.equal(actual, 'div { margin: 0; padding: 0 }'); + }); + }); + + it('Should remove units in zero-valued dimensions, test 2', function() { + let test = new Test(this, {'unitless-zero': true}); + return test.comb.processString( + 'div { margin: 0% }' + ).then(function(actual) { + assert.equal(actual, 'div { margin: 0 }'); + }); + }); + + it('Should remove units in zero-valued media-query params', function() { + let test = new Test(this, {'unitless-zero': true}); + return test.comb.processString( + '@media all and (min-width: 0px) { div { margin: 0em; padding: 0px } }' + ).then(function(actual) { + assert.equal(actual, '@media all and (min-width: 0) { div { margin: 0; padding: 0 } }'); + }); + }); + + it('Should not remove units (degs) in rotate property', function() { + let test = new Test(this, {'unitless-zero': true}); + return test.comb.processString( + 'div { -webkit-transform: rotate(0deg); }' + ).then(function(actual) { + assert.equal(actual, 'div { -webkit-transform: rotate(0deg); }'); + }); + }); + + it('Issue 394', function() { + let test = new Test(this, {'unitless-zero': true}); + return test.shouldBeEqual('issue-394.css', 'issue-394.expected.css'); + }); + }); + + describe('less', function() { + it('Issue 389', function() { + let test = new Test(this, {'unitless-zero': true}); + return test.shouldBeEqual('issue-389.less'); + }); + }); +}); diff --git a/test/options/vendor-prefix-align.js b/test/options/vendor-prefix-align.js deleted file mode 100644 index d9638b98..00000000 --- a/test/options/vendor-prefix-align.js +++ /dev/null @@ -1,112 +0,0 @@ -describe('options/vendor-prefix-align', function() { - beforeEach(function() { - this.filename = __filename; - this.comb.configure({ 'vendor-prefix-align': true }); - }); - - it('Should correctly align prefixes in properties', function() { - this.shouldBeEqual('property-align.css', 'property-align.expected.css'); - }); - - it('Should correctly align prefixes in values', function() { - this.shouldBeEqual('value-align.css', 'value-align.expected.css'); - }); - - it('Should not touch already align prefixes', function() { - this.shouldBeEqual('already-aligned.css', 'already-aligned.expected.css'); - }); - - it('Should correct align prefixes in preoperties and values at the same time', function() { - this.shouldBeEqual('both.css', 'both.expected.css'); - }); - - it('Should always correctly align prefixes', function() { - this.shouldBeEqual('complex.css', 'complex.expected.css'); - }); - - it('Shouldn not detect anything if there are no prefixed groups', function() { - this.shouldDetect( - ['vendor-prefix-align'], - 'a{ color: red }a{ -webkit-transform: translateZ(0) }', - {} - ); - }); - - it('Shouldn detect vendor-prefix-align as false in properties', function() { - this.shouldDetect( - ['vendor-prefix-align'], - this.readFile('property-align.css'), - { - 'vendor-prefix-align': false - } - ); - }); - - it('Shouldn detect vendor-prefix-align as true in properties', function() { - this.shouldDetect( - ['vendor-prefix-align'], - this.readFile('property-align.expected.css'), - { - 'vendor-prefix-align': true - } - ); - }); - - it('Shouldn detect vendor-prefix-align as false in values', function() { - this.shouldDetect( - ['vendor-prefix-align'], - this.readFile('value-align.css'), - { - 'vendor-prefix-align': false - } - ); - }); - - it('Shouldn detect vendor-prefix-align as true in values', function() { - this.shouldDetect( - ['vendor-prefix-align'], - this.readFile('value-align.expected.css'), - { - 'vendor-prefix-align': true - } - ); - }); - - it('Shouldn detect vendor-prefix-align as true, test 1', function() { - this.shouldDetect( - ['vendor-prefix-align'], - this.readFile('already-aligned.css'), - { - 'vendor-prefix-align': true - } - ); - }); - - it('Shouldn detect vendor-prefix-align as true, test 2', function() { - this.shouldDetect( - ['vendor-prefix-align'], - this.readFile('complex.expected.css'), - { - 'vendor-prefix-align': true - } - ); - }); - - it('Shouldn detect vendor-prefix-align as false', function() { - this.shouldDetect( - ['vendor-prefix-align'], - this.readFile('complex.css'), - { - 'vendor-prefix-align': false - } - ); - }); - - it('Should not detect anything in simple case', function() { - this.shouldDetect( - ['vendor-prefix-align'], - 'a{border:0;}', - {} - ); - }); -}); diff --git a/test/options/vendor-prefix-align/already-aligned.css b/test/options/vendor-prefix-align/detect/css/already-aligned.css similarity index 100% rename from test/options/vendor-prefix-align/already-aligned.css rename to test/options/vendor-prefix-align/detect/css/already-aligned.css diff --git a/test/options/vendor-prefix-align/complex.css b/test/options/vendor-prefix-align/detect/css/complex.css similarity index 100% rename from test/options/vendor-prefix-align/complex.css rename to test/options/vendor-prefix-align/detect/css/complex.css diff --git a/test/options/vendor-prefix-align/complex.expected.css b/test/options/vendor-prefix-align/detect/css/complex.expected.css similarity index 100% rename from test/options/vendor-prefix-align/complex.expected.css rename to test/options/vendor-prefix-align/detect/css/complex.expected.css diff --git a/test/options/vendor-prefix-align/property-align.css b/test/options/vendor-prefix-align/detect/css/property-align.css similarity index 100% rename from test/options/vendor-prefix-align/property-align.css rename to test/options/vendor-prefix-align/detect/css/property-align.css diff --git a/test/options/vendor-prefix-align/property-align.expected.css b/test/options/vendor-prefix-align/detect/css/property-align.expected.css similarity index 100% rename from test/options/vendor-prefix-align/property-align.expected.css rename to test/options/vendor-prefix-align/detect/css/property-align.expected.css diff --git a/test/options/vendor-prefix-align/value-align.css b/test/options/vendor-prefix-align/detect/css/value-align.css similarity index 100% rename from test/options/vendor-prefix-align/value-align.css rename to test/options/vendor-prefix-align/detect/css/value-align.css diff --git a/test/options/vendor-prefix-align/value-align.expected.css b/test/options/vendor-prefix-align/detect/css/value-align.expected.css similarity index 100% rename from test/options/vendor-prefix-align/value-align.expected.css rename to test/options/vendor-prefix-align/detect/css/value-align.expected.css diff --git a/test/options/vendor-prefix-align/detect/test.js b/test/options/vendor-prefix-align/detect/test.js new file mode 100644 index 00000000..ca683896 --- /dev/null +++ b/test/options/vendor-prefix-align/detect/test.js @@ -0,0 +1,86 @@ +let Test = require('../../option_test'); + +describe('Option `vendor-prefix-align`, detect', function() { + describe('css', function() { + it('Shouldn not detect anything if there are no prefixed groups', function() { + let test = new Test(this); + test.shouldDetect( + ['vendor-prefix-align'], + 'a{ color: red }a{ -webkit-transform: translateZ(0) }', + {} + ); + }); + + it('Shouldn detect vendor-prefix-align as false in properties', function() { + let test = new Test(this); + test.shouldDetect( + ['vendor-prefix-align'], + test.readFile('property-align.css'), + {'vendor-prefix-align': false} + ); + }); + + it('Shouldn detect vendor-prefix-align as true in properties', function() { + let test = new Test(this); + test.shouldDetect( + ['vendor-prefix-align'], + test.readFile('property-align.expected.css'), + {'vendor-prefix-align': true} + ); + }); + + it('Shouldn detect vendor-prefix-align as false in values', function() { + let test = new Test(this); + test.shouldDetect( + ['vendor-prefix-align'], + test.readFile('value-align.css'), + {'vendor-prefix-align': false} + ); + }); + + it('Shouldn detect vendor-prefix-align as true in values', function() { + let test = new Test(this); + test.shouldDetect( + ['vendor-prefix-align'], + test.readFile('value-align.expected.css'), + {'vendor-prefix-align': true} + ); + }); + + it('Shouldn detect vendor-prefix-align as true, test 1', function() { + let test = new Test(this); + test.shouldDetect( + ['vendor-prefix-align'], + test.readFile('already-aligned.css'), + {'vendor-prefix-align': true} + ); + }); + + it('Shouldn detect vendor-prefix-align as true, test 2', function() { + let test = new Test(this); + test.shouldDetect( + ['vendor-prefix-align'], + test.readFile('complex.expected.css'), + {'vendor-prefix-align': true} + ); + }); + + it('Shouldn detect vendor-prefix-align as false', function() { + let test = new Test(this); + test.shouldDetect( + ['vendor-prefix-align'], + test.readFile('complex.css'), + {'vendor-prefix-align': false} + ); + }); + + it('Should not detect anything in simple case', function() { + let test = new Test(this); + test.shouldDetect( + ['vendor-prefix-align'], + 'a{border:0;}', + {} + ); + }); + }); +}); diff --git a/test/options/vendor-prefix-align/lint/test.js b/test/options/vendor-prefix-align/lint/test.js new file mode 100644 index 00000000..e69de29b diff --git a/test/options/vendor-prefix-align/already-aligned.expected.css b/test/options/vendor-prefix-align/process/css/already-aligned.css similarity index 100% rename from test/options/vendor-prefix-align/already-aligned.expected.css rename to test/options/vendor-prefix-align/process/css/already-aligned.css diff --git a/test/options/vendor-prefix-align/process/css/already-aligned.expected.css b/test/options/vendor-prefix-align/process/css/already-aligned.expected.css new file mode 100644 index 00000000..35b7eeec --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/already-aligned.expected.css @@ -0,0 +1,11 @@ +.radio-button_theme_normal .radio-button__radio:before +{ + background: rgba(0,0,0,.4); + background: -webkit-linear-gradient(top, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + background: -moz-linear-gradient(top, rgba(0,0,0,.2) 0, rgba(0,0,0,.4) 100%); + background: -o-linear-gradient(top, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + background: linear-gradient(to bottom, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + + -moz-box-shadow: 0 1px 0 rgba(0,0,0,.07); + box-shadow: 0 1px 0 rgba(0,0,0,.07); +} diff --git a/test/options/vendor-prefix-align/both.css b/test/options/vendor-prefix-align/process/css/both.css similarity index 100% rename from test/options/vendor-prefix-align/both.css rename to test/options/vendor-prefix-align/process/css/both.css diff --git a/test/options/vendor-prefix-align/both.expected.css b/test/options/vendor-prefix-align/process/css/both.expected.css similarity index 100% rename from test/options/vendor-prefix-align/both.expected.css rename to test/options/vendor-prefix-align/process/css/both.expected.css diff --git a/test/options/vendor-prefix-align/process/css/complex.css b/test/options/vendor-prefix-align/process/css/complex.css new file mode 100644 index 00000000..3f896df2 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/complex.css @@ -0,0 +1,30 @@ +@media all and (min-width:0) +{ + .radio-button_theme_normal .radio-button__radio:before + { + background: rgba(0,0,0,.4); + background: -webkit-linear-gradient(top, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + background: -moz-linear-gradient(top, rgba(0,0,0,.2) 0, rgba(0,0,0,.4) 100%); + background: -o-linear-gradient(top, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + background: linear-gradient(to bottom, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + -moz-box-shadow: 0 1px 0 rgba(0,0,0,.07); + box-shadow: 0 1px 0 rgba(0,0,0,.07); + } + + /* :after — фон */ + .radio-button_theme_normal .radio-button__radio:after + { + background: #fff; + background: -webkit-linear-gradient(top, #fff 0,#eee 100%); + background: -moz-linear-gradient(top, #fff 0, #eee 100%); + background: -o-linear-gradient(top, #fff 0,#eee 100%); + background: linear-gradient(to bottom, #fff 0,#eee 100%); + } + + /* _focused_yes */ + .radio-button_theme_normal .radio-button__radio_focused_yes:before + { + -moz-box-shadow: 0 0 6px 2px rgba(255,204,0,.7), 0 1px 0 rgba(0,0,0,.07); + box-shadow: 0 0 6px 2px rgba(255,204,0,.7), 0 1px 0 rgba(0,0,0,.07); + } +} diff --git a/test/options/vendor-prefix-align/process/css/complex.expected.css b/test/options/vendor-prefix-align/process/css/complex.expected.css new file mode 100644 index 00000000..688ecd61 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/complex.expected.css @@ -0,0 +1,30 @@ +@media all and (min-width:0) +{ + .radio-button_theme_normal .radio-button__radio:before + { + background: rgba(0,0,0,.4); + background: -webkit-linear-gradient(top, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + background: -moz-linear-gradient(top, rgba(0,0,0,.2) 0, rgba(0,0,0,.4) 100%); + background: -o-linear-gradient(top, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + background: linear-gradient(to bottom, rgba(0,0,0,.2) 0,rgba(0,0,0,.4) 100%); + -moz-box-shadow: 0 1px 0 rgba(0,0,0,.07); + box-shadow: 0 1px 0 rgba(0,0,0,.07); + } + + /* :after — фон */ + .radio-button_theme_normal .radio-button__radio:after + { + background: #fff; + background: -webkit-linear-gradient(top, #fff 0,#eee 100%); + background: -moz-linear-gradient(top, #fff 0, #eee 100%); + background: -o-linear-gradient(top, #fff 0,#eee 100%); + background: linear-gradient(to bottom, #fff 0,#eee 100%); + } + + /* _focused_yes */ + .radio-button_theme_normal .radio-button__radio_focused_yes:before + { + -moz-box-shadow: 0 0 6px 2px rgba(255,204,0,.7), 0 1px 0 rgba(0,0,0,.07); + box-shadow: 0 0 6px 2px rgba(255,204,0,.7), 0 1px 0 rgba(0,0,0,.07); + } +} diff --git a/test/options/vendor-prefix-align/process/css/issue-193.css b/test/options/vendor-prefix-align/process/css/issue-193.css new file mode 100644 index 00000000..e0047dd4 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/issue-193.css @@ -0,0 +1,2 @@ +li {color: #7799c8; + font-size: 10px;} diff --git a/test/options/vendor-prefix-align/process/css/issue-193.expected.css b/test/options/vendor-prefix-align/process/css/issue-193.expected.css new file mode 100644 index 00000000..e0047dd4 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/issue-193.expected.css @@ -0,0 +1,2 @@ +li {color: #7799c8; + font-size: 10px;} diff --git a/test/options/vendor-prefix-align/process/css/issue-241.css b/test/options/vendor-prefix-align/process/css/issue-241.css new file mode 100644 index 00000000..e562a614 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/issue-241.css @@ -0,0 +1,6 @@ +* +{ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; +} diff --git a/test/options/vendor-prefix-align/process/css/issue-241.expected.css b/test/options/vendor-prefix-align/process/css/issue-241.expected.css new file mode 100644 index 00000000..bbc0e348 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/issue-241.expected.css @@ -0,0 +1,6 @@ +* +{ + -webkit-box-sizing: border-box; + -moz-box-sizing: border-box; + box-sizing: border-box; + } diff --git a/test/options/vendor-prefix-align/process/css/multiline-comments.css b/test/options/vendor-prefix-align/process/css/multiline-comments.css new file mode 100644 index 00000000..33c66c5e --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/multiline-comments.css @@ -0,0 +1,9 @@ +a +{ + -webkit-color: panda; + /* + * /\_/\ + * ( o.o ) + * > ^ < + */ -moz-color: panda; +} diff --git a/test/options/vendor-prefix-align/process/css/multiline-comments.expected.css b/test/options/vendor-prefix-align/process/css/multiline-comments.expected.css new file mode 100644 index 00000000..6bc01b39 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/multiline-comments.expected.css @@ -0,0 +1,9 @@ +a +{ + -webkit-color: panda; + /* + * /\_/\ + * ( o.o ) + * > ^ < + */ -moz-color: panda; +} diff --git a/test/options/vendor-prefix-align/process/css/one-line-2.css b/test/options/vendor-prefix-align/process/css/one-line-2.css new file mode 100644 index 00000000..6050079f --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/one-line-2.css @@ -0,0 +1 @@ +a{top:0} diff --git a/test/options/vendor-prefix-align/process/css/one-line-2.expected.css b/test/options/vendor-prefix-align/process/css/one-line-2.expected.css new file mode 100644 index 00000000..6050079f --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/one-line-2.expected.css @@ -0,0 +1 @@ +a{top:0} diff --git a/test/options/vendor-prefix-align/process/css/one-line.css b/test/options/vendor-prefix-align/process/css/one-line.css new file mode 100644 index 00000000..34265960 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/one-line.css @@ -0,0 +1 @@ +a{ -webkit-color:tomato; -moz-color:tomato; top:0 } diff --git a/test/options/vendor-prefix-align/process/css/one-line.expected.css b/test/options/vendor-prefix-align/process/css/one-line.expected.css new file mode 100644 index 00000000..34265960 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/one-line.expected.css @@ -0,0 +1 @@ +a{ -webkit-color:tomato; -moz-color:tomato; top:0 } diff --git a/test/options/vendor-prefix-align/process/css/property-align.css b/test/options/vendor-prefix-align/process/css/property-align.css new file mode 100644 index 00000000..b19d6b7c --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/property-align.css @@ -0,0 +1,8 @@ +a +{ + color: #fff; + + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} diff --git a/test/options/vendor-prefix-align/process/css/property-align.expected.css b/test/options/vendor-prefix-align/process/css/property-align.expected.css new file mode 100644 index 00000000..25808572 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/property-align.expected.css @@ -0,0 +1,8 @@ +a +{ + color: #fff; + + -webkit-border-radius: 3px; + -moz-border-radius: 3px; + border-radius: 3px; +} diff --git a/test/options/vendor-prefix-align/process/css/same-name.css b/test/options/vendor-prefix-align/process/css/same-name.css new file mode 100644 index 00000000..dbdc5c40 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/same-name.css @@ -0,0 +1,7 @@ +.test +{ + -webkit-transition: -webkit-transform .6s; + transition: transform .6s; + -webkit-transform: scale(.6, .6); + transform: scale(.6, .6); +} diff --git a/test/options/vendor-prefix-align/process/css/same-name.expected.css b/test/options/vendor-prefix-align/process/css/same-name.expected.css new file mode 100644 index 00000000..90598073 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/same-name.expected.css @@ -0,0 +1,7 @@ +.test +{ + -webkit-transition: -webkit-transform .6s; + transition: transform .6s; + -webkit-transform: scale(.6, .6); + transform: scale(.6, .6); +} diff --git a/test/options/vendor-prefix-align/process/css/value-align.css b/test/options/vendor-prefix-align/process/css/value-align.css new file mode 100644 index 00000000..f15a3620 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/value-align.css @@ -0,0 +1,10 @@ +b +{ + color: #fff; + + background: -webkit-linear-gradient(linear, left top, right top, color-stop(0,rgba(255,255,255,0)), color-stop(20%, #fff)); + background: -moz-linear-gradient(left, rgba(255,255,255,0) 0, #fff 20%); + background: -o-linear-gradient(left, rgba(255,255,255,0) 0, #fff 20%); + background: -ms-linear-gradient(left, rgba(255,255,255,0) 0, #fff 20%); + background: linear-gradient(to right, rgba(255,255,255,0) 0, #fff 20%); +} diff --git a/test/options/vendor-prefix-align/process/css/value-align.expected.css b/test/options/vendor-prefix-align/process/css/value-align.expected.css new file mode 100644 index 00000000..60aba2ee --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/value-align.expected.css @@ -0,0 +1,10 @@ +b +{ + color: #fff; + + background: -webkit-linear-gradient(linear, left top, right top, color-stop(0,rgba(255,255,255,0)), color-stop(20%, #fff)); + background: -moz-linear-gradient(left, rgba(255,255,255,0) 0, #fff 20%); + background: -o-linear-gradient(left, rgba(255,255,255,0) 0, #fff 20%); + background: -ms-linear-gradient(left, rgba(255,255,255,0) 0, #fff 20%); + background: linear-gradient(to right, rgba(255,255,255,0) 0, #fff 20%); +} diff --git a/test/options/vendor-prefix-align/process/css/with-comment-property-2.css b/test/options/vendor-prefix-align/process/css/with-comment-property-2.css new file mode 100644 index 00000000..113d3342 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/with-comment-property-2.css @@ -0,0 +1,3 @@ +a +{ + top: 0;/* ololo */margin: 0;} diff --git a/test/options/vendor-prefix-align/process/css/with-comment-property-2.expected.css b/test/options/vendor-prefix-align/process/css/with-comment-property-2.expected.css new file mode 100644 index 00000000..113d3342 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/with-comment-property-2.expected.css @@ -0,0 +1,3 @@ +a +{ + top: 0;/* ololo */margin: 0;} diff --git a/test/options/vendor-prefix-align/process/css/with-comment-property.css b/test/options/vendor-prefix-align/process/css/with-comment-property.css new file mode 100644 index 00000000..9e3bea36 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/with-comment-property.css @@ -0,0 +1,5 @@ +body +{ + -webkit-transition: transform 150ms linear; + /* comment */ transition: transform 150ms linear; +} diff --git a/test/options/vendor-prefix-align/process/css/with-comment-property.expected.css b/test/options/vendor-prefix-align/process/css/with-comment-property.expected.css new file mode 100644 index 00000000..b65b2105 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/with-comment-property.expected.css @@ -0,0 +1,5 @@ +body +{ + -webkit-transition: transform 150ms linear; + /* comment */ transition: transform 150ms linear; +} diff --git a/test/options/vendor-prefix-align/process/css/with-comment.css b/test/options/vendor-prefix-align/process/css/with-comment.css new file mode 100644 index 00000000..f792a8cc --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/with-comment.css @@ -0,0 +1,5 @@ +body +{ + background: -webkit-linear-gradient(nani); + background: /* comment */ -moz-linear-gradient(nani); +} diff --git a/test/options/vendor-prefix-align/process/css/with-comment.expected.css b/test/options/vendor-prefix-align/process/css/with-comment.expected.css new file mode 100644 index 00000000..9d5d161c --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/with-comment.expected.css @@ -0,0 +1,5 @@ +body +{ + background: -webkit-linear-gradient(nani); + background: /* comment */ -moz-linear-gradient(nani); +} diff --git a/test/options/vendor-prefix-align/process/css/without-space.css b/test/options/vendor-prefix-align/process/css/without-space.css new file mode 100644 index 00000000..09bd955f --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/without-space.css @@ -0,0 +1,5 @@ +body +{ + background:-webkit-linear-gradient(nani); + background:-moz-linear-gradient(nani); +} diff --git a/test/options/vendor-prefix-align/process/css/without-space.expected.css b/test/options/vendor-prefix-align/process/css/without-space.expected.css new file mode 100644 index 00000000..1be53893 --- /dev/null +++ b/test/options/vendor-prefix-align/process/css/without-space.expected.css @@ -0,0 +1,5 @@ +body +{ + background:-webkit-linear-gradient(nani); + background: -moz-linear-gradient(nani); +} diff --git a/test/options/vendor-prefix-align/process/sass/property.sass b/test/options/vendor-prefix-align/process/sass/property.sass new file mode 100644 index 00000000..b488c7a2 --- /dev/null +++ b/test/options/vendor-prefix-align/process/sass/property.sass @@ -0,0 +1,4 @@ +a + -webkit-color: panda + color: panda + -moz-color: panda diff --git a/test/options/vendor-prefix-align/process/sass/value.expected.sass b/test/options/vendor-prefix-align/process/sass/value.expected.sass new file mode 100644 index 00000000..4df59dad --- /dev/null +++ b/test/options/vendor-prefix-align/process/sass/value.expected.sass @@ -0,0 +1,4 @@ +a + color: panda + color: -webkit-panda + color: -moz-panda diff --git a/test/options/vendor-prefix-align/process/sass/value.sass b/test/options/vendor-prefix-align/process/sass/value.sass new file mode 100644 index 00000000..e3cff569 --- /dev/null +++ b/test/options/vendor-prefix-align/process/sass/value.sass @@ -0,0 +1,4 @@ +a + color: panda + color: -webkit-panda + color: -moz-panda diff --git a/test/options/vendor-prefix-align/process/test.js b/test/options/vendor-prefix-align/process/test.js new file mode 100644 index 00000000..1b4d70ac --- /dev/null +++ b/test/options/vendor-prefix-align/process/test.js @@ -0,0 +1,95 @@ +let Test = require('../../option_test'); + +describe('Option `vendor-prefix-align`, process', function() { + describe('css', function() { + it('Should correctly work when there is comment before property name', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('with-comment-property.css', 'with-comment-property.expected.css'); + }); + + it('Should correctly work when there is comment before property name. Test 2', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('with-comment-property-2.css', 'with-comment-property-2.expected.css'); + }); + + it('Should correctly work when there is comment before property name. Test 3', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('multiline-comments.css', 'multiline-comments.expected.css'); + }); + + it('Should correctly align prefixes in properties', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('property-align.css', 'property-align.expected.css'); + }); + + it('Should correctly align prefixes in values', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('value-align.css', 'value-align.expected.css'); + }); + + it('Should not touch already align prefixes', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('already-aligned.css', 'already-aligned.expected.css'); + }); + + it('Should correctly align prefixes in properties and values at the same time', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('both.css', 'both.expected.css'); + }); + + it('Should correctly work when value and property names are the same', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('same-name.css', 'same-name.expected.css'); + }); + + it('Should correctly work when there is no whitespace after colon', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('without-space.css', 'without-space.expected.css'); + }); + + it('Should correctly work when there is comment after colon', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('with-comment.css', 'with-comment.expected.css'); + }); + + it('Should not do anything with oneliners', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('one-line.css', 'one-line.expected.css'); + }); + + it('Should not do anything with oneliners. Test 2', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('one-line-2.css', 'one-line-2.expected.css'); + }); + + it('Should always correctly align prefixes', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('complex.css', 'complex.expected.css'); + }); + + it('Issue 193: should handle declarations without preceding spaces', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('issue-193.css', 'issue-193.expected.css'); + }); + + it('Issue 241: should not break tabs', function() { + let test = new Test(this, { + 'block-indent': '\t', + 'vendor-prefix-align': true + }); + return test.shouldBeEqual('issue-241.css', 'issue-241.expected.css'); + }); + }); + + describe('sass', function() { + it('Should align prexied values', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('value.sass', 'value.expected.sass'); + }); + + it('Should not align prefixed property names', function() { + let test = new Test(this, {'vendor-prefix-align': true}); + return test.shouldBeEqual('property.sass'); + }); + }); +});