diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md index f3f3389f..df19e10e 100644 --- a/.github/ISSUE_TEMPLATE.md +++ b/.github/ISSUE_TEMPLATE.md @@ -1,4 +1,22 @@ - -**Prettier 1.9.2** +**Prettier 1.11.1** [Playground link](https://prettier.io/playground/#.....) ```sh # Options (if any): diff --git a/.github/no-response.yml b/.github/no-response.yml new file mode 100644 index 00000000..c6086946 --- /dev/null +++ b/.github/no-response.yml @@ -0,0 +1,10 @@ +# Configuration for probot-no-response - https://github.com/probot/no-response + +daysUntilClose: 14 +responseRequiredLabel: "status:awaiting response" +closeComment: > + This issue has been automatically closed because there has been no response + to our request for more information from the original author. With only the + information that is currently in the issue, we don't have enough information + to take action. Please reach out if you have or find the answers we need so + that we can investigate further. diff --git a/.gitignore b/.gitignore index d5d61155..d94c796e 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ .DS_Store coverage .idea +package-lock.json diff --git a/.pre-commit-hooks.yaml b/.pre-commit-hooks.yaml index cfad8722..abc1d981 100644 --- a/.pre-commit-hooks.yaml +++ b/.pre-commit-hooks.yaml @@ -2,5 +2,5 @@ name: prettier entry: prettier --write language: node - # From https://github.com/prettier/prettier/blob/133303f47a30f6b3e46ffdf9d5c2d6609d65c416/src/options.js#L32-L42 - files: \.(css|less|scss|html|ts|tsx|graphql|gql|json|js|jsx)$ + # From https://github.com/prettier/prettier/blob/7a7eb170/docs/index.md + files: \.(css|less|scss|html|ts|tsx|graphql|gql|json|js|jsx|md)$ diff --git a/.travis.yml b/.travis.yml index fab2daf3..67cab0b8 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,7 +6,7 @@ node_js: cache: yarn: true directories: - - node_modules + - node_modules env: - NODE_ENV=development - NODE_ENV=production @@ -20,5 +20,9 @@ before_script: script: - yarn lint - yarn lint-docs - - AST_COMPARE=1 yarn test -- --runInBand + - if [ "${NODE_ENV}" = "production" ]; then yarn test:dist; fi + - if [ "${NODE_ENV}" = "development" ]; then AST_COMPARE=1 yarn test -- --runInBand; fi - if [ "${NODE_ENV}" = "development" ]; then yarn codecov; fi +branches: + only: + - master diff --git a/CHANGELOG.md b/CHANGELOG.md index 102ae7ce..1be808ce 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,32 @@ +# 1.11.1 + +[link](https://github.com/prettier/prettier/compare/1.11.0...1.11.1) + +* 1.11.0 was incorrectly shipped with the wrong version of the TypeScript parser, which broke conditional types. This release fixes it. +* Fixed an issue relating to deprecated parsers ([#4072](https://github.com/prettier/prettier/pull/4072)) + +# 1.11.0 + +* [Release Notes](https://prettier.io/blog/2018/02/26/1.11.0.html) + +# 1.10.2 + +[link](https://github.com/prettier/prettier/compare/1.10.1...1.10.2) + +* Fixed an issue printing .vue files with self-closing tags. (#3705 by duailibe) + +# 1.10.1 + +[link](https://github.com/prettier/prettier/compare/1.10.0...1.10.1) + +* Fixed an issue where the CLI fails to resolve a file. + +# 1.10.0 + +[link](https://github.com/prettier/prettier/compare/1.9.2...1.10.0) + +* [Release Notes](https://prettier.io/blog/2018/01/10/1.10.0.html) + # 1.9.2 [link](https://github.com/prettier/prettier/compare/1.9.1...1.9.2) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 6516a919..2476889f 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ yarn test Here's what you need to know about the tests: -* The tests uses [Jest](https://facebook.github.io/jest/) snapshots. +* The tests use [Jest snapshots](https://facebook.github.io/jest/docs/en/snapshot-testing.html). * You can make changes and run `jest -u` (or `yarn test -u`) to update the snapshots. Then run `git diff` to take a look at what changed. Always update the snapshots when opening a PR. * You can run `AST_COMPARE=1 jest` for a more robust test run. That formats each file, re-parses it, and compares the new AST with the original one and makes sure they are semantically equivalent. * Each test folder has a `jsfmt.spec.js` that runs the tests. For JavaScript files, generally you can just put `run_spec(__dirname, ["babylon", "flow", "typescript"]);` there. This will verify that the output using each parser is the same. You can also pass options as the third argument, like this: `run_spec(__dirname, ["babylon"], { trailingComma: "es5" });` diff --git a/LICENSE b/LICENSE index adc9379a..3d50578e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright 2017 James Long +Copyright 2017-2018 James Long Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/README.md b/README.md index de966e3e..05b70bbf 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,9 @@ · GraphQL · JSON · Markdown + · + Your favorite language? +

@@ -23,7 +26,7 @@ Gitter - Travis + Travis Codecov diff --git a/commands.md b/commands.md index e54ceb55..c3149dc7 100644 --- a/commands.md +++ b/commands.md @@ -22,7 +22,7 @@ declare function group(doc: Doc, opts?: GroupOpts): Doc; Mark a group of items which the printer should try to fit on one line. This is the basic command to tell the printer when to break. Groups are usually nested, and the printer will try to fit everything on one line, but if it doesn't fit it will break the outermost group first and try again. It will continue breaking groups until everything fits (or there are no more groups to break). -A document can force parent groups to break by including `breakParent` (see below). A hard and literal line automatically include this so they always break parent groups. Breaks are propagated to all parent groups, so if a deeply nested expression has a hard break, everything with break. This only matters for "hard" breaks, i.e. newlines that are printed no matter what and can be statically analyzed. +A document can force parent groups to break by including `breakParent` (see below). A hard and literal line automatically include this so they always break parent groups. Breaks are propagated to all parent groups, so if a deeply nested expression has a hard break, everything will break. This only matters for "hard" breaks, i.e. newlines that are printed no matter what and can be statically analyzed. For example, an array will try to fit on one line: @@ -193,13 +193,49 @@ declare function indent(doc: Doc): Doc; Increase the level of indentation. +### dedent + +```ts +declare function dedent(doc: Doc): Doc; +``` + +Decrease the level of indentation. (Each `align` is considered one level of indentation.) + ### align ```ts -declare function align(n: number, doc: Doc): Doc; +declare function align(n: number | string, doc: Doc): Doc; ``` -This is similar to indent but it increases the level of indentation by a fixed number. When using tabs, it's going to print spaces. You should prefer using `indent` whenever possible. +This is similar to indent but it increases the level of indentation by a fixed number or a string. +Trailing alignments in indentation are still spaces, but middle ones are transformed into one tab per `align` when `useTabs` enabled. +If it's using in a whitespace-sensitive language, e.g. markdown, you should use `n` with string value to force print it. + +For example: + +* `useTabs` + * `tabWidth: 2` + * `` -> `<2 space>` + * `` -> `<2 space>` + * `tabWidth: 4` + * `` -> `<2 space>` + * `` -> `<2 space>` + +### markAsRoot + +```ts +declare function markAsRoot(doc: Doc): Doc; +``` + +This marks the current indentation as root for `dedentToRoot` and `literalline`s. + +#### dedentToRoot + +```ts +declare function dedentToRoot(doc: Doc): Doc; +``` + +This will dedent the current indentation to the root marked by `markAsRoot`. ### cursor diff --git a/docs/assets/webstorm/external-tool-prettier.png b/docs/assets/webstorm/external-tool-prettier.png new file mode 100644 index 00000000..e56fa41c Binary files /dev/null and b/docs/assets/webstorm/external-tool-prettier.png differ diff --git a/docs/assets/webstorm/file-watcher-prettier.png b/docs/assets/webstorm/file-watcher-prettier.png new file mode 100644 index 00000000..3fd6b548 Binary files /dev/null and b/docs/assets/webstorm/file-watcher-prettier.png differ diff --git a/docs/assets/webstorm/prettier-file-watcher.png b/docs/assets/webstorm/prettier-file-watcher.png deleted file mode 100644 index e3c89634..00000000 Binary files a/docs/assets/webstorm/prettier-file-watcher.png and /dev/null differ diff --git a/docs/assets/webstorm/with-prettier.png b/docs/assets/webstorm/with-prettier.png deleted file mode 100644 index ce8d71e4..00000000 Binary files a/docs/assets/webstorm/with-prettier.png and /dev/null differ diff --git a/docs/assets/youtube-cover/a-prettier-printer-by-james-long-on-react-conf-2017.png b/docs/assets/youtube-cover/a-prettier-printer-by-james-long-on-react-conf-2017.png new file mode 100644 index 00000000..c2d064ec Binary files /dev/null and b/docs/assets/youtube-cover/a-prettier-printer-by-james-long-on-react-conf-2017.png differ diff --git a/docs/assets/youtube-cover/javascript-code-formatting-by-christopher-chedeau-on-react-london-2017.png b/docs/assets/youtube-cover/javascript-code-formatting-by-christopher-chedeau-on-react-london-2017.png new file mode 100644 index 00000000..3aa1576f Binary files /dev/null and b/docs/assets/youtube-cover/javascript-code-formatting-by-christopher-chedeau-on-react-london-2017.png differ diff --git a/docs/editors.md b/docs/editors.md index 99ebe0d0..7d71bd6e 100644 --- a/docs/editors.md +++ b/docs/editors.md @@ -17,11 +17,9 @@ Vim users can simply install either [sbdchd](https://github.com/sbdchd)/[neoform ## Visual Studio Code -Can be installed using the extension sidebar. Search for `Prettier - Code formatter`. +`prettier-vscode` can be installed using the extension sidebar. Search for `Prettier - Code formatter`. It can also be installed using `ext install prettier-vscode` in the command palette. [Check its repository for configuration and shortcuts](https://github.com/prettier/prettier-vscode). -Can also be installed using `ext install prettier-vscode`. - -[Check its repository for configuration and shortcuts](https://github.com/prettier/prettier-vscode) +If you'd like to toggle the formatter on and off, install [`vscode-status-bar-format-toggle`](https://marketplace.visualstudio.com/items?itemName=tombonnike.vscode-status-bar-format-toggle). ## Visual Studio diff --git a/docs/eslint.md b/docs/eslint.md index 93915efc..b4ab0e4a 100644 --- a/docs/eslint.md +++ b/docs/eslint.md @@ -57,3 +57,9 @@ There are a few rules that this disables that you may want to turn back on as lo "extends": ["plugin:prettier/recommended"] } ``` + +Remember to install both `eslint-plugin-prettier` and `eslint-config-prettier`: + +```bash +yarn add --dev eslint-plugin-prettier eslint-config-prettier +``` diff --git a/docs/ignore.md b/docs/ignore.md index b69c4cc0..caa02444 100644 --- a/docs/ignore.md +++ b/docs/ignore.md @@ -7,7 +7,7 @@ Prettier offers an escape hatch to ignore a block of code or prevent entire file ## Ignoring Files -To exclude files from formatting, add entries to a `.prettierignore` file in the project root or set the `--ignore-path` [CLI](cli.md) option. +To exclude files from formatting, add entries to a `.prettierignore` file in the project root or set the [`--ignore-path` CLI option](cli.md#ignore-path). ## JavaScript @@ -69,3 +69,24 @@ matrix( Do not format this ``` + +### Range Ignore + +_available in v1.12.0+_ + +This type of ignore is only allowed to be used in top-level and aimed to disable formatting for auto-generated content, e.g. [`all-contributors`](https://github.com/kentcdodds/all-contributors), [`markdown-toc`](https://github.com/jonschlinkert/markdown-toc), etc. + + + + +```markdown + + + +| MY | AWESOME | AUTO-GENERATED | TABLE | +|-|-|-|-| +| a | b | c | d | + + + +``` diff --git a/docs/index.md b/docs/index.md index d98adb32..cdf019b4 100644 --- a/docs/index.md +++ b/docs/index.md @@ -46,9 +46,9 @@ Prettier enforces a consistent code **style** (i.e. code formatting that won't a If you want to learn more, these two conference talks are great introductions: -[![](https://cloud.githubusercontent.com/assets/197597/24886367/dda8a6f0-1e08-11e7-865b-22492450f10f.png)](https://www.youtube.com/watch?v=hkfBvpEfWdA) +[![A Prettier Printer by James Long on React Conf 2017](/docs/assets/youtube-cover/a-prettier-printer-by-james-long-on-react-conf-2017.png)](https://www.youtube.com/watch?v=hkfBvpEfWdA) -[![](https://cloud.githubusercontent.com/assets/197597/24886368/ddacd6f8-1e08-11e7-806a-9febd23cbf47.png)](https://www.youtube.com/watch?v=0Q4kUNx85_4") +[![JavaScript Code Formatting by Christopher Chedeau on React London 2017](/docs/assets/youtube-cover/javascript-code-formatting-by-christopher-chedeau-on-react-london-2017.png)](https://www.youtube.com/watch?v=0Q4kUNx85_4) #### Footnotes diff --git a/docs/install.md b/docs/install.md index cf01c38e..24c1e6ba 100644 --- a/docs/install.md +++ b/docs/install.md @@ -7,11 +7,7 @@ Install with `yarn`: ```bash yarn add prettier --dev --exact -``` - -You can install it globally if you like: - -```bash +# or globally yarn global add prettier ``` diff --git a/docs/option-philosophy.md b/docs/option-philosophy.md new file mode 100644 index 00000000..6fbd89f1 --- /dev/null +++ b/docs/option-philosophy.md @@ -0,0 +1,28 @@ +--- +id: option-philosophy +title: Option Philosophy +--- + +Prettier is not a kitchen-sink code formatter that attempts to print your code in any way you wish. It is _opinionated._ Quoting the [Why Prettier?](why-prettier.md) page: + +> By far the biggest reason for adopting Prettier is to stop all the on-going debates over styles. + +The more options Prettier has, the further from the above goal it gets. **The debates over styles just turn into debates over which Prettier options to use.** + +The issue about [resisting adding configuration](https://github.com/prettier/prettier/issues/40) has more 👍s than any option request issue. + +So why does Prettier have options at all? + +Well, had Prettier been created around the same time as JavaScript itself was born it could have made choices that the community would have picked up (which is the case for [elm-format](https://github.com/avh4/elm-format/)). But JavaScript is far older than Prettier so the community has had time to start their holy wars about tabs vs spaces, single vs double quotes, indentation levels, trailing commas and semicolons, so Prettier more or less has to support those. + +Then there's a bunch of interesting cases. + +* `--trailing-comma es5` was added to make it easier to use trailing commas in most environments without having to transpile (trailing function commas were added in ES2017). +* `--prose-wrap` is important to support all quirky markdown renderers in the wild. +* `--arrow-parens` was added after [huge demand](https://github.com/prettier/prettier/issues/812). Prettier has to strike a balance between ideal goals and listening to the community. +* `--jsx-bracket-same-line` was needed for a big company with a huge code base (Facebook), which backed the project when it got started, to be able to [adopt Prettier at all](https://github.com/prettier/prettier/pull/661#issuecomment-295770645). + +Finally, perhaps the most interesting of them all is `--bracket-spacing`. +The truth is that not even [Prettier's creator knows exactly why it exists](https://github.com/prettier/prettier/issues/715#issuecomment-281096495). It was added super early on without much thought. It now serves as an example of the types of options we should avoid. + +Remember, it is easy to _add_ features to a program, but hard to remove them. diff --git a/docs/plugins.md b/docs/plugins.md index 7738a3c3..449f8de2 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -1,50 +1,59 @@ --- id: plugins -title: Plugins +title: Plugins (Beta) --- -# IN DEVELOPMENT +## IN BETA -> The plugin API is unreleased and the API may change! +> The plugin API is in a **beta** state as of Prettier 1.10 and the API may change in the next release! Plugins are ways of adding new languages to Prettier. Prettier's own implementations of all languages are expressed using the plugin API. The core `prettier` package contains JavaScript and other web-focussed languages built in. For additional languages you'll need to install a plugin. ## Using Plugins -There are three ways to add plugins to Prettier: +Plugins are automatically loaded if you have them installed in your `package.json`. Prettier plugin package names must start with `@prettier/plugin-` or `prettier-plugin-` to be registered. -* Via the CLI. -* Via the API. -* With a configuration file. +If the plugin is unable to be found automatically, you can load them with: -### Configuration File (Recommended) +* The [CLI](./cli.md), via the `--plugin` flag: -In your [configuration file](./configuration.md), add the `plugins` property: + ```bash + prettier --write main.foo --plugin=./foo-plugin + ``` -```json -{ - "plugins": ["prettier-python"] -} -``` + > Tip: You can pass multiple `--plugin` flags. -### CLI +* Or the [API](./api.md), via the `plugins` field: -With the [CLI](./cli.md), pass the `--plugin` flag: - -```bash -prettier --write main.py --plugin prettier-python -``` - -> Tip: You can pass multiple `--plugin` flags. + ```js + prettier.format("code", { + parser: "foo", + plugins: ["./foo-plugin"] + }); + ``` ## Official Plugins -* [`prettier-python`](https://github.com/prettier/prettier-python) -* [`prettier-php`](https://github.com/prettier/prettier-php) +* [`@prettier/plugin-python`](https://github.com/prettier/plugin-python) +* [`@prettier/plugin-php`](https://github.com/prettier/plugin-php) +* [`@prettier/plugin-swift`](https://github.com/prettier/plugin-swift) + +## Community Plugins + +* [`prettier-plugin-elm`](https://github.com/gicentre/prettier-plugin-elm) by [**@giCentre**](https://github.com/gicentre) +* [`prettier-plugin-java`](https://github.com/thorbenvh8/prettier-java) by [**@thorbenvh8**](https://github.com/thorbenvh8) +* [`prettier-plugin-pg`](https://github.com/benjie/prettier-plugin-pg) by [**@benjie**](https://github.com/benjie) +* [`prettier-plugin-ruby`](https://github.com/iamsolankiamit/prettier-ruby) by [**@iamsolankiamit**](https://github.com/iamsolankiamit) ## Developing Plugins -Prettier plugins are regular JavaScript modules with three exports, `languages`, `parsers` and `printers`. +Prettier plugins are regular JavaScript modules with five exports: + +* `languages` +* `parsers` +* `printers` +* `options` +* `defaultOptions` ### `languages` @@ -68,14 +77,17 @@ export const languages = [ Parsers convert code as a string into an [AST](https://en.wikipedia.org/wiki/Abstract_syntax_tree). -The key must match the name in the `parsers` array from `languages`. The value contains a parse function and an AST format name. +The key must match the name in the `parsers` array from `languages`. The value contains a parse function, an AST format name, and two location extraction functions (`locStart` and `locEnd`). ```js export const parsers = { "dance-parse": { parse, // The name of the AST that - astFormat: "dance-ast" + astFormat: "dance-ast", + hasPragma, + locStart, + locEnd } }; ``` @@ -86,6 +98,18 @@ The signature of the `parse` function is: function parse(text: string, parsers: object, options: object): AST; ``` +The location extraction functions (`locStart` and `locEnd`) return the starting and ending locations of a given AST node: + +```ts +function locStart(node: object): number; +``` + +The pragma detection function (`hasPragma`) should return if the text contains the pragma comment. + +```ts +function hasPragma(text: string): boolean; +``` + ### `printers` Printers convert ASTs into a Prettier intermediate representation, also known as a Doc. @@ -96,12 +120,13 @@ The key must match the `astFormat` that the parser produces. The value contains export const printers = { "dance-ast": { print, - embed + embed, + insertPragma } }; ``` -Printing is a recursive process of coverting an AST node (represented by a path to that node) into a doc. The doc is constructed using the [builder commands](https://github.com/prettier/prettier/blob/master/commands.md): +Printing is a recursive process of converting an AST node (represented by a path to that node) into a doc. The doc is constructed using the [builder commands](https://github.com/prettier/prettier/blob/master/commands.md): ```js const { concat, join, line, ifBreak, group } = require("prettier").doc.builders; @@ -139,6 +164,51 @@ function embed( If you don't want to switch to a different parser, simply return `null` or `undefined`. +A plugin can implement how a pragma comment is inserted in the resulting code when the `--insert-pragma` option is used, in the `insertPragma` function. Its signature is: + +```ts +function insertPragma(text: string): string; +``` + +### `options` + +`options` is an object containing the custom options your plugin supports. + +Example: + +```js +options: { + openingBraceNewLine: { + type: "boolean", + category: "Global", + default: true, + description: "Move open brace for code blocks onto new line." + } +} +``` + +### `defaultOptions` + +If your plugin requires different default values for some of Prettier's core options, you can specify them in `defaultOptions`: + +``` +defaultOptions: { + tabWidth: 4 +} +``` + +### Utility functions + +A `util` module from Prettier core is considered a private API and is not meant to be consumed by plugins. Instead, the `util-shared` module provides the following limited set of utility functions for plugins: + +```ts +makeString(rawContent: string, enclosingQuote: string, unescapeUnnecessarEscapes: boolean): string; +getNextNonSpaceNonCommentCharacterIndex(text: string, node: object, options: object): number; +isNextLineEmptyAfterIndex(text: string, index: number): boolean; +isNextLineEmpty(text: string, node: object, options: object): boolean; +mapDoc(doc: object, callback: function): void; +``` + ## Testing Plugins Since plugins can be resolved using relative paths, when working on one you can do: @@ -152,4 +222,4 @@ prettier.format(code, { }); ``` -This will resolve a plugin relative to the current working direcrory. +This will resolve a plugin relative to the current working directory. diff --git a/docs/precommit.md b/docs/precommit.md index 262e895b..7164c2ee 100644 --- a/docs/precommit.md +++ b/docs/precommit.md @@ -7,6 +7,8 @@ You can use Prettier with a pre-commit tool. This can re-format your files that ## Option 1. [lint-staged](https://github.com/okonet/lint-staged) +**Use Case:** Useful for when you need to use other tools on top of Prettier (e.g. ESLint) + Install it along with [husky](https://github.com/typicode/husky): ```bash @@ -30,31 +32,31 @@ and add this config to your `package.json`: See https://github.com/okonet/lint-staged#configuration for more details about how you can configure lint-staged. -## Option 2. [pre-commit](https://github.com/observing/pre-commit) (JS version) +## Option 2. [pretty-quick](https://github.com/azz/pretty-quick) -Install the package: +**Use Case:** Great for when you want an entire file formatting on your changed/staged files. + +Install it along with [husky](https://github.com/typicode/husky): ```bash -yarn add pre-commit --dev +yarn add pretty-quick husky --dev ``` and add this config to your `package.json`: - ```json { "scripts": { - "prettier": "prettier \"*/**/*.js\" --ignore-path ./.prettierignore --write && git add . && git status" - }, - "pre-commit": [ - "prettier" - ] + "precommit": "pretty-quick --staged" + } } ``` -Find more info from [here](https://github.com/observing/pre-commit). +Find more info from [here](https://github.com/azz/pretty-quick). -## Option 3. [pre-commit](https://github.com/pre-commit/pre-commit) (Python version) +## Option 3. [pre-commit](https://github.com/pre-commit/pre-commit) + +**Use Case:** Great when working with multi-language projects. Copy the following config into your `.pre-commit-config.yaml` file: @@ -65,9 +67,33 @@ Copy the following config into your `.pre-commit-config.yaml` file: - id: prettier ``` -Find more info from [here](http://pre-commit.com). +Find more info from [here](https://pre-commit.com). -## Option 4. bash script +## Option 4. [precise-commits](https://github.com/JamesHenry/precise-commits) + +**Use Case:** Great for when you want an partial file formatting on your changed/staged files. + +Install it along with [husky](https://github.com/typicode/husky): + +```bash +yarn add precise-commits husky --dev +``` + +and add this config to your `package.json`: + +```json +{ + "scripts": { + "precommit": "precise-commits" + } +} +``` + +**Note:** This is currently the only tool that will format only staged lines rather than the entire file. See more information [here](https://github.com/JamesHenry/precise-commits#why-precise-commits) + +Read more about this tool [here](https://github.com/JamesHenry/precise-commits#2-precommit-hook). + +## Option 5. bash script Alternately you can save this script as `.git/hooks/pre-commit` and give it execute permission: @@ -84,3 +110,12 @@ echo "$jsfiles" | xargs git add exit 0 ``` + +If git is reporting that your prettified files are still modified after committing, you may need to add a post-commit script to update git's index as described in [this issue](https://github.com/prettier/prettier/issues/2978#issuecomment-334408427). + +Add something like the following to `.git/hooks/post-commit`: + +```bash +#!/bin/sh +git update-index -g +``` diff --git a/docs/rationale.md b/docs/rationale.md index 2bdded20..8953de4d 100644 --- a/docs/rationale.md +++ b/docs/rationale.md @@ -3,78 +3,146 @@ id: rationale title: Rationale --- -Prettier is an opinionated code formatter. This document gives a rationale behind those opinions. +Prettier is an opinionated code formatter. This document explains some of its choices. -## What prettier is concerned about - -### Consistency - -Prettier exists for one purpose: to enforce consistency across your entire project. Not only do we output code with consistent whitespace, prettier will lay out code according to a wrapping algorithm based on a maximum line width. That means that long expressions will be broken up across lines, removing the need for manual layout from the programmer which inevitably leads to inconsistency. +## What Prettier is concerned about ### Correctness -The first requirement of prettier is to output valid JavaScript and code that has the exact same behavior as before formatting. Please report any JavaScript code where prettier fails to follow these correctness rules — that's a bug which needs to be fixed! - -### Whitespace: indentation and line breaks - -This is the core of prettier. The formatting rules are going to be explained in a later section. +The first requirement of Prettier is to output valid code that has the exact same behavior as before formatting. Please report any code where Prettier fails to follow these correctness rules — that's a bug which needs to be fixed! ### Strings -Prettier enforces double quotes by default, but has a setting for enforcing single quotes instead. There are two exceptions: +Double or single quotes? Prettier chooses the one which results in the fewest number of escapes. `"It's gettin' better!"`, not `'It\'s gettin\' better!'`. In case of a tie, Prettier defaults to double quotes (but that can be changed via the [singleQuote](options.html#quotes) option). -* The number of escaped quotes are minimized. For example, if you have a string with a single quote inside, it will be enclosed in double quotes regardless of the quote setting: `"that's a double quote"`, not `'that\'s a double quote'`. -* JSX always uses double quotes. JSX takes its roots from HTML, where the dominant use of quotes for attributes is double quotes. Browser developer tools also follow this convention by always displaying HTML with double quotes, even if the source code uses single quotes. +JSX always uses double quotes. JSX takes its roots from HTML, where the dominant use of quotes for attributes is double quotes. Browser developer tools also follow this convention by always displaying HTML with double quotes, even if the source code uses single quotes. Prettier maintains the way your string is escaped. For example, `"🙂"` won't be formatted into `"\uD83D\uDE42"` and vice versa. -### Parentheses - -Prettier outputs the minimum number of parentheses required to ensure that the behavior of the formatted code stays unchanged. This may lead to code that feels ambiguous. If that's the case, you are encouraged to extract the ambiguous parts into variables. - ### Empty lines -It turns out that empty lines are very hard to automatically generate. The approach that prettier takes is to preserve empty lines the way they were in the original source code. The only constraint is that prettier disallows several empty lines in a row. They are collapsed to a single one. +It turns out that empty lines are very hard to automatically generate. The approach that Prettier takes is to preserve empty lines the way they were in the original source code. There are two additional rules: + +* Prettier collapses multiple blank lines into a single blank line. +* Empty lines at the start and end of blocks (and whole files) are removed. (Files always end with a single newline, though.) ### Multi-line objects -It is tempting to collapse an object to a single line if it fits, but there are times when it is better for sibling/cousin keys to stay vertically aligned—see [object lists], [nested configs], [stylesheets], and [keyed methods]. To avoid unfavorable collapsing, prettier simply formats any object as multi-line if it appears as such in the original source code. This is the same strategy used by [elm-format] for multi-line records. +By default, Prettier’s printing algorithm prints expressions on a single line if they fit. Objects are used for a lot of different things in JavaScript, though, and sometimes it really helps readability if they stay multiline. See [object lists], [nested configs], [stylesheets] and [keyed methods], for example. We haven't been able to find a good rule for all those cases, so Prettier instead keeps objects multiline if there's a newline anywhere inside it in the original source code. A consequence of this is that long singleline objects are automatically expanded, but short multiline objects are never collapsed. [object lists]: https://github.com/prettier/prettier/issues/74#issue-199965534 [nested configs]: https://github.com/prettier/prettier/issues/88#issuecomment-275448346 [stylesheets]: https://github.com/prettier/prettier/issues/74#issuecomment-275262094 [keyed methods]: https://github.com/prettier/prettier/pull/495#issuecomment-275745434 -[elm-format]: https://github.com/prettier/prettier/issues/74#issuecomment-275621526 -## What prettier is _not_ concerned about +### Semicolons -Here are a few examples of things that are out of scope for prettier: +This is about using the `--no-semi` option. -* Turning single/double quotes into template literals or vice versa. -* Adding/removing `{}` and `return` where they are optional. -* Turning `?:` into `if then else`. +Consider this piece of code: - +```js +if (shouldAddLines) { + [-1, 1].forEach(delta => addLine(delta * 20)) +} +``` -...TBD... +While the above code works just fine without semicolons, Prettier actually turns it into: -## Formatting rules + +```js +if (shouldAddLines) { + ;[-1, 1].forEach(delta => addLine(delta * 20)) +} +``` -... TBD ... +This is to help you avoid mistakes. Imagine adding this line: +```diff + if (shouldAddLines) { ++ console.log('Do we even get here??') + [-1, 1].forEach(delta => addLine(delta * 20)) + } +``` -### Function calls +Oops! The above actually means: + +```js +if (shouldAddLines) { + console.log('Do we even get here??')[-1, 1].forEach(delta => addLine(delta * 20)) +} +``` -### Method calls +With a semicolon in front of that `[` such issues never happen. It makes the line independent of other lines so you can move and add lines without having to think about ASI rules. +This practice is also common in [standard] which uses a semicolon-free style. + +[standard]: https://standardjs.com/rules.html#semicolons + +### Imports + +Prettier can break long `import` statements across several lines: + +```js +import { + CollectionDashboard, + DashboardPlaceholder +} from "../components/collections/collection-dashboard/main"; +``` + +The following example doesn't fit within the print width, but Prettier prints it in a single line anyway: + +```js +import { CollectionDashboard } from "../components/collections/collection-dashboard/main"; +``` + +This might be unexpected by some, but we do it this way since it was a common request to keep `import`s with single elements in a single line. The same applies for `require` calls. ### JSX +Prettier prints things a little differently compared to other JS when JSX is involved: -### Boolean expressions +```jsx +function greet(user) { + return user + ? `Welcome back, ${user.name}!` + : "Greetings, traveler! Sign up today!"; +} +function Greet({ user }) { + return ( +
+ {user ? ( +

Welcome back, {user.name}!

+ ) : ( +

Greetings, traveler! Sign up today!

+ )} +
+ ); +} +``` -### String concatenation ---> +There are two reasons. + +First off, lots of people already wrapped their JSX in parentheses, especially in `return` statements. Prettier follows this common style. + +Secondly, [the alternate formatting makes it easier to edit the JSX](https://github.com/prettier/prettier/issues/2208). It is easy to leave a semicolon behind. As opposed to normal JS, a leftover semicolon in JSX can end up as plain text showing on your page. + +```jsx +
+

Greetings, traveler! Sign up today!

; {/* <-- Oops! */} +
+``` + +## What Prettier is _not_ concerned about + +Prettier only _prints_ code. It does not transform it. This is to limit the scope of Prettier. Let's focus on the printing and do it really well! + +Here are a few examples of things that are out of scope for Prettier: + +* Turning single- or double-quoted strings into template literals or vice versa. +* Adding/removing `{}` and `return` where they are optional. +* Turning `?:` into `if`-`else` statements. +* Sorting and hoisting `import`s. (Sorting is unsafe because of side effects, which would violate the [correctness](#correctness) goal.) diff --git a/docs/related-projects.md b/docs/related-projects.md index 24404417..38d0977f 100644 --- a/docs/related-projects.md +++ b/docs/related-projects.md @@ -34,3 +34,4 @@ title: Related Projects * [`prettier-github`](https://github.com/jgierer12/prettier-github) formats code in GitHub comments * [`rollup-plugin-prettier`](https://github.com/mjeanroy/rollup-plugin-prettier) allows you to use Prettier with Rollup * [`markdown-magic-prettier`](https://github.com/camacho/markdown-magic-prettier) allows you to use Prettier to format JS [codeblocks](https://help.github.com/articles/creating-and-highlighting-code-blocks/) in Markdown files via [Markdown Magic](https://github.com/DavidWells/markdown-magic) +* [`pretty-quick`](https://github.com/azz/pretty-quick) formats your changed files with Prettier diff --git a/docs/vim.md b/docs/vim.md index f2281081..861f4cf9 100644 --- a/docs/vim.md +++ b/docs/vim.md @@ -77,9 +77,9 @@ When installed via vim-plug, a default prettier executable is installed inside v vim-prettier executable resolution: -1. Traverse parents and search for Prettier installation inside `node_modules` -2. Look for a global prettier installation -3. Use locally installed vim-prettier prettier executable +1. Traverse parents and search for Prettier installation inside `node_modules` +2. Look for a global prettier installation +3. Use locally installed vim-prettier prettier executable ### vim-prettier - Usage diff --git a/docs/webstorm.md b/docs/webstorm.md index 80f6fdb3..e3c5f066 100644 --- a/docs/webstorm.md +++ b/docs/webstorm.md @@ -1,60 +1,56 @@ --- id: webstorm -title: Webstorm Setup +title: WebStorm Setup --- -## SetUp +## WebStorm 2018.1 and above -### With ESLint Integration +Use the `Reformat with Prettier` action (`Alt-Shift-Cmd-P` on macOS or `Alt-Shift-Ctrl-P` on Windows and Linux) to format the selected code, a file, or a whole directory. -If you are using the ESLint integration for prettier via [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier) all you need to do is simply add a hotkey for `eslint --fix`. To do this go to _File | Settings | Keymap_ for Windows and Linux _WebStorm | Preferences | Keymap_, type `Fix ESLint Problems` in search box and add a keyboard shortcut. +Don't forget to install `prettier` first. -See [this documentation](https://www.jetbrains.com/help/webstorm/configuring-keyboard-shortcuts.html) about configuring keyboard shortcuts. +To use Prettier in IntelliJ IDEA, PhpStorm, PyCharm, and other JetBrains IDEs, please install this [plugin](https://plugins.jetbrains.com/plugin/10456-prettier). -### Standalone +For older IDE versions, please follow the instructions below. -#### Configure External Tool +## Running Prettier on save using File Watcher -https://blog.jetbrains.com/webstorm/2016/08/using-external-tools/ +To automatically format your files using `prettier` on save, you can use a file watcher. -Go to _File | Settings | Tools | External Tools_ for Windows and Linux or _WebStorm | Preferences | Tools | External Tools_ for OS X and click **+** to add a new tool. Let’s name it **Prettier**. +Go to _Preferences | Tools | File Watchers_ and click **+** to add a new watcher. Let’s name it **Prettier**. -* **Program** set `prettier` +* **File Type**: _JavaScript_ (or _Any_ if you want to run `prettier` on all files) +* **Scope**: _Project Files_ +* **Program**: full path to `.bin/prettier` or `.bin\prettier.cmd` in the project's `node_module` folder +* **Arguments**: `--write [other options] $FilePathRelativeToProjectRoot$` +* **Output paths to refresh**: `$FilePathRelativeToProjectRoot$` +* **Working directory**: `$ProjectFileDir$` +* **Auto-save edited files to trigger the watcher**: Uncheck to reformat on Save only. -> If on the other hand you have `prettier` installed locally, replace the **Program** with `$ProjectFileDir$/node_modules/.bin/prettier` (on OS X and Linux) or `$ProjectFileDir$\node_modules\.bin\prettier.cmd` (on Windows). +![Example](/docs/assets/webstorm/file-watcher-prettier.png) -* **Parameters** set `--write [other opts] $FilePathRelativeToProjectRoot$` -* **Working directory** set `$ProjectFileDir$` +## WebStorm 2017.3 or earlier -![Example](/docs/assets/webstorm/with-prettier.png) +### Using Prettier with ESLint -##### Process directories +If you are using ESLint with [eslint-plugin-prettier](https://github.com/prettier/eslint-plugin-prettier), use the `Fix ESLint Problems` action to reformat the currect file – find it using _Find Action_ (`Cmd/Ctrl-Shift-A`) or [add a keyboard shortcut](https://www.jetbrains.com/help/webstorm/configuring-keyboard-shortcuts.html) to it in _Preferences | Kymap_ and then use it. -* Clone the External tool created above and name it `Prettier Directories` -* **Parameters** set `--write [other opts] $FileDirRelativeToProjectRoot$/**/{*.js,*.jsx}` +Make sure that the ESLint integration is enabled in _Preferences | Languages & Frameworks | JavaScript | Code Quality Tools | ESLint_. -#### Usage +### Using Prettier as External Tool -* Cmd-Shift-A on OS X or Ctrl+Shift+A on Windows and Linux -* Type: 'prettier' and hit enter +Go to _Preferences | Tools | External Tools_ and click **+** to add a new tool. Let’s name it **Prettier**. -#### Configure Keymap +* **Program**: `prettier` (if it's installed globally) +* **Parameters**: `--write [other options] $FilePathRelativeToProjectRoot$` +* **Working directory**: `$ProjectFileDir$` -Now when you setup **External Tool** I guess you want to add hotkey for it. Go to _File | Settings | Keymap_ for Windows and Linux _WebStorm | Preferences | Keymap_ and type external tool name in search box. +> If Prettier is installed locally in your project, replace the **Program** with `$ProjectFileDir$/node_modules/.bin/prettier` (on macOS and Linux) or `$ProjectFileDir$\node_modules\.bin\prettier.cmd` (on Windows). -See [this documentation](https://www.jetbrains.com/help/webstorm/configuring-keyboard-shortcuts.html) about configuring keyboard shortcuts. +![Example](/docs/assets/webstorm/external-tool-prettier.png) -## Using File Watcher +Press `Cmd/Ctrl-Shift-A` (_Find Action_), search for _Prettier_, and then hit `Enter`. -To automatically format using `prettier` on save, you can use a file watcher. +It will run `prettier` for the current file. -Go to _File | Settings | Tools | File Watchers_ for Windows and Linux or _WebStorm | Preferences | Tools | File Watchers_ for OS X and click **+** to add a new tool. Let’s name it **Prettier**. - -* **File Type**: JavaScript -* **Scope**: Current File -* **Program** set `prettier` (if you have `prettier` installed locally, see ["Configure External Tool"](#configure-external-tool) above) -* **Arguments** set `--write [other opts] $FilePath$` -* **Working directory** set `$ProjectFileDir$` -* **Immediate file synchronization**: Uncheck to reformat on Save only (otherwise code will jump around while you type). - -![Example](/docs/assets/webstorm/prettier-file-watcher.png) +You can [add a keyboard shortcut](https://www.jetbrains.com/help/webstorm/configuring-keyboard-shortcuts.html) to run this External tool configuration in _Preferences | Keymap_. diff --git a/docs/why-prettier.md b/docs/why-prettier.md index 7bf1d4e2..c420055b 100644 --- a/docs/why-prettier.md +++ b/docs/why-prettier.md @@ -7,6 +7,8 @@ title: Why Prettier? By far the biggest reason for adopting Prettier is to stop all the on-going debates over styles. It is generally accepted that having a common style guide is valuable for a project and team but getting there is a very painful and unrewarding process. People get very emotional around particular ways of writing code and nobody likes spending time writing and receiving nits. +So why choose the "Prettier style guide" over any other random style guide? Because Prettier is the only "style guide" that is fully automatic. Even if Prettier does not format all code 100% the way you'd like, it's worth the "sacrifice" given the unique benefits of Prettier, don't you think? + * “We want to free mental threads and end discussions around style. While sometimes fruitful, these discussions are for the most part wasteful.” * “Literally had an engineer go through a huge effort of cleaning up all of our code because we were debating ternary style for the longest time and were inconsistent about it. It was dumb, but it was a weird on-going "great debate" that wasted lots of little back and forth bits. It's far easier for us all to agree now: just run Prettier, and go with that style.” * “Getting tired telling people how to style their product code.” diff --git a/index.js b/index.js index 677e1c21..115f670d 100644 --- a/index.js +++ b/index.js @@ -1,10 +1,9 @@ "use strict"; -const docblock = require("jest-docblock"); - const version = require("./package.json").version; -const util = require("./src/common/util"); +const privateUtil = require("./src/common/util"); +const sharedUtil = require("./src/common/util-shared"); const getSupportInfo = require("./src/common/support").getSupportInfo; const comments = require("./src/main/comments"); @@ -37,11 +36,6 @@ function attachComments(text, ast, opts) { return astComments; } -function hasPragma(text) { - const pragmas = Object.keys(docblock.parse(docblock.extract(text))); - return pragmas.indexOf("prettier") !== -1 || pragmas.indexOf("format") !== -1; -} - function ensureAllCommentsPrinted(astComments) { if (!astComments) { return; @@ -68,7 +62,9 @@ function ensureAllCommentsPrinted(astComments) { } function formatWithCursor(text, opts, addAlignmentSize) { - if (opts.requirePragma && !hasPragma(text)) { + const selectedParser = parser.resolveParser(opts); + const hasPragma = !selectedParser.hasPragma || selectedParser.hasPragma(text); + if (opts.requirePragma && !hasPragma) { return { formatted: text }; } @@ -80,24 +76,19 @@ function formatWithCursor(text, opts, addAlignmentSize) { if ( opts.insertPragma && - !hasPragma(text) && + opts.printer.insertPragma && + !hasPragma && opts.rangeStart === 0 && opts.rangeEnd === Infinity ) { - const parsedDocblock = docblock.parseWithComments(docblock.extract(text)); - const pragmas = Object.assign({ format: "" }, parsedDocblock.pragmas); - const newDocblock = docblock.print({ - pragmas, - comments: parsedDocblock.comments.replace(/^(\s+?\r?\n)+/, "") // remove leading newlines - }); - const strippedText = docblock.strip(text); - const separatingNewlines = strippedText.startsWith("\n") ? "\n" : "\n\n"; - text = newDocblock + separatingNewlines + strippedText; + text = opts.printer.insertPragma(text); } addAlignmentSize = addAlignmentSize || 0; - const ast = parser.parse(text, opts); + const result = parser.parse(text, opts); + const ast = result.ast; + text = result.text; const formattedRangeOnly = formatRange(text, opts, ast); if (formattedRangeOnly) { @@ -109,7 +100,7 @@ function formatWithCursor(text, opts, addAlignmentSize) { const cursorNodeAndParents = findNodeAtOffset(ast, opts.cursorOffset, opts); const cursorNode = cursorNodeAndParents.node; if (cursorNode) { - cursorOffset = opts.cursorOffset - util.locStart(cursorNode); + cursorOffset = opts.cursorOffset - opts.locStart(cursorNode); opts.cursorNode = cursorNode; } } @@ -143,7 +134,7 @@ function format(text, opts, addAlignmentSize) { return formatWithCursor(text, opts, addAlignmentSize).formatted; } -function findSiblingAncestors(startNodeAndParents, endNodeAndParents) { +function findSiblingAncestors(startNodeAndParents, endNodeAndParents, opts) { let resultStartNode = startNodeAndParents.node; let resultEndNode = endNodeAndParents.node; @@ -158,7 +149,7 @@ function findSiblingAncestors(startNodeAndParents, endNodeAndParents) { if ( endParent.type !== "Program" && endParent.type !== "File" && - util.locStart(endParent) >= util.locStart(startNodeAndParents.node) + opts.locStart(endParent) >= opts.locStart(startNodeAndParents.node) ) { resultEndNode = endParent; } else { @@ -170,7 +161,7 @@ function findSiblingAncestors(startNodeAndParents, endNodeAndParents) { if ( startParent.type !== "Program" && startParent.type !== "File" && - util.locEnd(startParent) <= util.locEnd(endNodeAndParents.node) + opts.locEnd(startParent) <= opts.locEnd(endNodeAndParents.node) ) { resultStartNode = startParent; } else { @@ -187,8 +178,8 @@ function findSiblingAncestors(startNodeAndParents, endNodeAndParents) { function findNodeAtOffset(node, offset, options, predicate, parentNodes) { predicate = predicate || (() => true); parentNodes = parentNodes || []; - const start = util.locStart(node); - const end = util.locEnd(node); + const start = options.locStart(node, options.locStart); + const end = options.locEnd(node, options.locEnd); if (start <= offset && offset <= end) { for (const childNode of comments.getSortedChildNodes( node, @@ -248,10 +239,10 @@ function isSourceElement(opts, node) { "ExportNamedDeclaration", // Module "ExportAllDeclaration", // Module "TypeAlias", // Flow - "InterfaceDeclaration", // Flow, Typescript - "TypeAliasDeclaration", // Typescript - "ExportAssignment", // Typescript - "ExportDeclaration" // Typescript + "InterfaceDeclaration", // Flow, TypeScript + "TypeAliasDeclaration", // TypeScript + "ExportAssignment", // TypeScript + "ExportDeclaration" // TypeScript ]; const jsonSourceElements = [ "ObjectExpression", @@ -333,12 +324,19 @@ function calculateRange(text, opts, ast) { const siblingAncestors = findSiblingAncestors( startNodeAndParents, - endNodeAndParents + endNodeAndParents, + opts ); const startNode = siblingAncestors.startNode; const endNode = siblingAncestors.endNode; - const rangeStart = Math.min(util.locStart(startNode), util.locStart(endNode)); - const rangeEnd = Math.max(util.locEnd(startNode), util.locEnd(endNode)); + const rangeStart = Math.min( + opts.locStart(startNode, opts.locStart), + opts.locStart(endNode, opts.locStart) + ); + const rangeEnd = Math.max( + opts.locEnd(startNode, opts.locEnd), + opts.locEnd(endNode, opts.locEnd) + ); return { rangeStart: rangeStart, @@ -365,7 +363,10 @@ function formatRange(text, opts, ast) { ); const indentString = text.slice(rangeStart2, rangeStart); - const alignmentSize = util.getAlignmentSize(indentString, opts.tabWidth); + const alignmentSize = privateUtil.getAlignmentSize( + indentString, + opts.tabWidth + ); const rangeFormatted = format( rangeString, @@ -411,6 +412,8 @@ module.exports = { version, + util: sharedUtil, + /* istanbul ignore next */ __debug: { parse: function(text, opts) { @@ -432,7 +435,9 @@ module.exports = { }, printToDoc: function(text, opts) { opts = normalizeOptions(opts); - const ast = parser.parse(text, opts); + const result = parser.parse(text, opts); + const ast = result.ast; + text = result.text; attachComments(text, ast, opts); const doc = printAstToDoc(ast, opts); return doc; diff --git a/jest.config.js b/jest.config.js index bcfffd31..c774d0b4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -14,5 +14,12 @@ module.exports = { "/src/clean-ast.js", "/src/deprecated.js" ], + moduleNameMapper: { + // Jest wires `fs` to `graceful-fs`, which causes a memory leak when + // `graceful-fs` does `require('fs')`. + // Ref: https://github.com/facebook/jest/issues/2179#issuecomment-355231418 + // If this is removed, see also rollup.bin.config.js and rollup.index.config.js. + "graceful-fs": "/tests_config/fs.js" + }, transform: {} }; diff --git a/package.json b/package.json index 15b8e4d7..61d47e05 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "prettier", - "version": "1.9.2", + "version": "1.11.1", "description": "Prettier is an opinionated code formatter", "bin": { "prettier": "./bin/prettier.js" @@ -14,7 +14,7 @@ "node": ">=4" }, "dependencies": { - "@babel/code-frame": "7.0.0-beta.35", + "@babel/code-frame": "7.0.0-beta.40", "@glimmer/syntax": "0.30.3", "babylon": "7.0.0-beta.34", "camelcase": "4.1.0", @@ -24,36 +24,39 @@ "dashify": "0.2.2", "dedent": "0.7.0", "diff": "3.2.0", - "editorconfig": "0.14.2", + "editorconfig": "0.15.0", "editorconfig-to-prettier": "0.0.6", "emoji-regex": "6.5.1", "escape-string-regexp": "1.0.5", "esutils": "2.0.2", "find-project-root": "1.1.1", - "flow-parser": "0.59.0", + "flow-parser": "0.64.0", "get-stream": "3.0.0", "globby": "6.1.0", - "graphql": "0.10.5", + "graphql": "0.13.2", + "gray-matter": "3.1.1", + "html-tag-names": "1.1.2", "ignore": "3.3.7", - "jest-docblock": "21.3.0-beta.11", - "jest-validate": "21.1.0", + "jest-docblock": "22.2.2", + "json-stable-stringify": "1.0.1", "leven": "2.1.0", "mem": "1.1.0", "minimatch": "3.0.4", "minimist": "1.2.0", "parse5": "3.0.3", - "postcss-less": "1.1.3", + "postcss-less": "1.1.5", "postcss-media-query-parser": "0.2.3", - "postcss-scss": "1.0.2", + "postcss-scss": "1.0.5", "postcss-selector-parser": "2.2.3", - "postcss-values-parser": "1.3.1", + "postcss-values-parser": "1.5.0", + "read-pkg-up": "3.0.0", "remark-frontmatter": "1.1.0", - "remark-parse": "4.0.0", + "remark-parse": "5.0.0", "resolve": "1.5.0", "semver": "5.4.1", "string-width": "2.1.1", - "typescript": "2.7.0-insiders.20171214", - "typescript-eslint-parser": "11.0.0", + "typescript": "2.8.0-rc", + "typescript-eslint-parser": "14.0.0", "unicode-regex": "1.0.1", "unified": "6.1.6" }, @@ -62,15 +65,15 @@ "babel-preset-es2015": "6.24.1", "codecov": "2.2.0", "cross-env": "5.0.5", - "eslint": "4.1.1", + "eslint": "4.18.2", "eslint-config-prettier": "2.9.0", "eslint-friendly-formatter": "3.0.0", - "eslint-plugin-import": "2.6.1", - "eslint-plugin-prettier": "2.4.0", - "eslint-plugin-react": "7.1.0", + "eslint-plugin-import": "2.9.0", + "eslint-plugin-prettier": "2.6.0", + "eslint-plugin-react": "7.7.0", "jest": "21.1.0", "mkdirp": "0.5.1", - "prettier": "1.9.2", + "prettier": "1.11.1", "prettylint": "1.0.0", "rimraf": "2.6.2", "rollup": "0.47.6", @@ -80,18 +83,19 @@ "rollup-plugin-node-globals": "1.1.0", "rollup-plugin-node-resolve": "2.0.0", "rollup-plugin-replace": "1.2.1", - "shelljs": "0.7.8", + "shelljs": "0.8.1", "snapshot-diff": "0.2.2", "strip-ansi": "4.0.0", "sw-toolbox": "3.6.0", - "uglify-es": "3.0.28", + "tempy": "0.2.1", + "uglify-es": "3.3.9", "webpack": "2.6.1" }, "scripts": { "prepublishOnly": "echo \"Error: must publish from dist/\" && exit 1", "prepare-release": "yarn && yarn build && yarn test:dist", "test": "jest", - "test:dist": "cross-env NODE_ENV=production yarn test", + "test:dist": "node ./scripts/test-dist.js", "test-integration": "jest tests_integration", "lint": "cross-env EFF_NO_LINK_RULES=true eslint . --format node_modules/eslint-friendly-formatter", "lint-docs": "prettylint {.,docs,website,website/blog}/*.md", diff --git a/scripts/build/build-docs.js b/scripts/build/build-docs.js index bee13cfa..de870920 100644 --- a/scripts/build/build-docs.js +++ b/scripts/build/build-docs.js @@ -2,6 +2,7 @@ "use strict"; +const fs = require("fs"); const path = require("path"); const shell = require("shelljs"); const parsers = require("./parsers"); @@ -39,6 +40,13 @@ shell.exec( `node_modules/babel-cli/bin/babel.js ${docs}/index.js --out-file ${docs}/index.js --presets=es2015` ); +// wrap content with IIFE to avoid `assign to readonly` error on Safari +(function(filename) { + const content = fs.readFileSync(filename, "utf8"); + const wrapped = `"use strict";(function(){${content}}());`; + fs.writeFileSync(filename, wrapped); +})(`${docs}/index.js`); + shell.exec( `rollup -c scripts/build/rollup.docs.config.js --environment filepath:parser-babylon.js -i ${prettierPath}/parser-babylon.js` ); @@ -62,6 +70,16 @@ shell.cp("node_modules/sw-toolbox/sw-toolbox.js", `${docs}/sw-toolbox.js`); shell.cd("website"); shell.echo("Building website..."); shell.exec("yarn install"); + +shell.echo("Copy prettier-animated-logo CSS file to docs"); +shell.cp( + path.join( + rootDir, + "website/node_modules/@sandhose/prettier-animated-logo/dist/wide.css" + ), + `${docs}/prettier-animated-logo.css` +); + shell.exec("yarn build"); shell.echo(); diff --git a/scripts/build/build.js b/scripts/build/build.js index f4d6a7ff..5d009cac 100755 --- a/scripts/build/build.js +++ b/scripts/build/build.js @@ -26,7 +26,7 @@ shell.rm("-Rf", "dist/"); shell.exec("rollup -c scripts/build/rollup.index.config.js"); shell.exec("rollup -c scripts/build/rollup.bin.config.js"); -shell.chmod("+x", "./dist/bin/prettier.js"); +shell.chmod("+x", "./dist/bin-prettier.js"); shell.exec("rollup -c scripts/build/rollup.third-party.config.js"); @@ -63,7 +63,7 @@ shell.sed( /eval\("require"\)/, "require", "dist/index.js", - "dist/bin/prettier.js" + "dist/bin-prettier.js" ); shell.echo("Update ISSUE_TEMPLATE.md"); @@ -86,11 +86,13 @@ pipe(newIssueTemplate).to(".github/ISSUE_TEMPLATE.md"); shell.echo("Copy package.json"); const pkgWithoutDependencies = Object.assign({}, pkg); +pkgWithoutDependencies.bin = "./bin-prettier.js"; delete pkgWithoutDependencies.dependencies; pkgWithoutDependencies.scripts = { prepublishOnly: "node -e \"assert.equal(require('.').version, require('..').version)\"" }; +pkgWithoutDependencies.files = ["*.js"]; pipe(JSON.stringify(pkgWithoutDependencies, null, 2)).to("dist/package.json"); shell.echo("Copy README.md"); diff --git a/scripts/build/rollup.bin.config.js b/scripts/build/rollup.bin.config.js index 1844407e..2f0600c5 100644 --- a/scripts/build/rollup.bin.config.js +++ b/scripts/build/rollup.bin.config.js @@ -7,13 +7,20 @@ import * as path from "path"; export default Object.assign(baseConfig, { entry: "bin/prettier.js", - dest: "dist/bin/prettier.js", + dest: "dist/bin-prettier.js", format: "cjs", banner: "#!/usr/bin/env node", plugins: [ - replace({ "#!/usr/bin/env node": "" }), + replace({ + "#!/usr/bin/env node": "", + // See comment in jest.config.js + "require('graceful-fs')": "require('fs')" + }), json(), - resolve({ preferBuiltins: true }), + resolve({ + preferBuiltins: true, + extensions: [".js", ".json"] + }), commonjs() ], external: [ @@ -27,6 +34,6 @@ export default Object.assign(baseConfig, { path.resolve("src/common/third-party.js") ], paths: { - [path.resolve("src/common/third-party.js")]: "../third-party" + [path.resolve("src/common/third-party.js")]: "./third-party" } }); diff --git a/scripts/build/rollup.index.config.js b/scripts/build/rollup.index.config.js index 80a8f9c1..74c821bf 100644 --- a/scripts/build/rollup.index.config.js +++ b/scripts/build/rollup.index.config.js @@ -17,10 +17,15 @@ export default Object.assign(baseConfig, { format: "cjs", plugins: [ replace({ - "process.env.NODE_ENV": JSON.stringify("production") + "process.env.NODE_ENV": JSON.stringify("production"), + // See comment in jest.config.js + "require('graceful-fs')": "require('fs')" }), json(), - resolve({ preferBuiltins: true }), + resolve({ + preferBuiltins: true, + extensions: [".js", ".json"] + }), commonjs() ], external, diff --git a/scripts/build/rollup.parser.config.js b/scripts/build/rollup.parser.config.js index 4386c97d..f923f010 100644 --- a/scripts/build/rollup.parser.config.js +++ b/scripts/build/rollup.parser.config.js @@ -24,7 +24,7 @@ export default Object.assign(baseConfig, { // by its value before bundling. parser.endsWith("flow") ? replace({ - "require(s8)": 'require("fs")', + "require(tf)": 'require("fs")', include: "node_modules/flow-parser/flow_parser.js" }) : {}, diff --git a/scripts/generate-schema.js b/scripts/generate-schema.js new file mode 100755 index 00000000..798668a7 --- /dev/null +++ b/scripts/generate-schema.js @@ -0,0 +1,102 @@ +#!/usr/bin/env node + +"use strict"; + +const prettier = require(".."); + +console.log( + prettier.format( + JSON.stringify(generateSchema(prettier.getSupportInfo().options)), + { parser: "json" } + ) +); + +function generateSchema(options) { + return { + $schema: "http://json-schema.org/draft-04/schema#", + title: "Schema for .prettierrc", + type: "object", + definitions: { + optionsDefinition: { + type: "object", + properties: options.reduce( + (props, option) => + Object.assign(props, { [option.name]: optionToSchema(option) }), + {} + ) + }, + overridesDefinition: { + type: "object", + properties: { + overrides: { + type: "array", + description: + "Provide a list of patterns to override prettier configuration.", + items: { + type: "object", + required: ["files"], + properties: { + files: { + description: "Include these files in this override.", + oneOf: [ + { type: "string" }, + { type: "array", items: { type: "string" } } + ] + }, + excludeFiles: { + description: "Exclude these files from this override.", + oneOf: [ + { type: "string" }, + { type: "array", items: { type: "string" } } + ] + }, + options: { + type: "object", + description: "The options to apply for this override.", + $ref: "#/definitions/optionsDefinition" + } + }, + additionalProperties: false + } + } + } + } + }, + allOf: [ + { $ref: "#/definitions/optionsDefinition" }, + { $ref: "#/definitions/overridesDefinition" } + ] + }; +} + +function optionToSchema(option) { + return Object.assign( + { + description: option.description, + default: option.default + }, + option.type === "choice" + ? { oneOf: option.choices.map(choiceToSchema) } + : { type: optionTypeToSchemaType(option.type) } + ); +} + +function optionTypeToSchemaType(optionType) { + switch (optionType) { + case "int": + return "integer"; + case "array": + case "boolean": + return optionType; + case "choice": + throw new Error( + "Please use `oneOf` instead of `enum` for better description support." + ); + default: + return "string"; + } +} + +function choiceToSchema(choice) { + return { enum: [choice.value], description: choice.description }; +} diff --git a/scripts/test-dist.js b/scripts/test-dist.js new file mode 100644 index 00000000..78e03c7d --- /dev/null +++ b/scripts/test-dist.js @@ -0,0 +1,33 @@ +#!/usr/bin/env node + +"use strict"; + +const path = require("path"); +const shell = require("shelljs"); +const tempy = require("tempy"); + +shell.config.fatal = true; + +const rootDir = path.join(__dirname, ".."); +const distDir = path.join(rootDir, "dist"); + +const file = shell.exec("npm pack", { cwd: distDir }).stdout.trim(); +const tarPath = path.join(distDir, file); +const tmpDir = tempy.directory(); + +shell.config.silent = true; +shell.exec("npm init -y", { cwd: tmpDir }); +shell.exec(`npm install "${tarPath}"`, { cwd: tmpDir }); +shell.config.silent = false; + +const code = shell.exec("yarn test --color --runInBand", { + cwd: rootDir, + env: Object.assign({}, process.env, { + NODE_ENV: "production", + AST_COMPARE: "1", + PRETTIER_DIR: path.join(tmpDir, "node_modules/prettier") + }), + shell: true +}).code; + +process.exit(code); diff --git a/src/cli/constant.js b/src/cli/constant.js index 7bf45adc..c2578d49 100644 --- a/src/cli/constant.js +++ b/src/cli/constant.js @@ -1,6 +1,5 @@ "use strict"; -const camelCase = require("camelcase"); const dedent = require("dedent"); const CATEGORY_CONFIG = "Config"; @@ -64,46 +63,18 @@ const categoryOrder = [ * // If the option has a value that is an exception to the regular value * // constraints, indicate that value here (or use a function for more * // flexibility). - * exception?: any | ((value: any) => boolean); + * exception?: ((value: any) => boolean); * * // Indicate that the option is deprecated. Use a string to add an extra * // message to --help for the option, for example to suggest a replacement * // option. * deprecated?: true | string; - * - * // Custom function to get the value for the option. Useful for handling - * // deprecated options. - * // --parser example: (value, argv) => argv["flow-parser"] ? "flow" : value - * getter?: (value: any, argv: any) => any; * } * } * * Note: The options below are sorted alphabetically. */ -const detailedOptions = normalizeDetailedOptions({ - "arrow-parens": { - type: "choice", - category: CATEGORY_FORMAT, - forwardToApi: true, - description: "Include parentheses around a sole arrow function parameter.", - choices: [ - { - value: "avoid", - description: "Omit parens when possible. Example: `x => x`" - }, - { - value: "always", - description: "Always include parens. Example: `(x) => x`" - } - ] - }, - "bracket-spacing": { - type: "boolean", - category: CATEGORY_FORMAT, - forwardToApi: true, - description: "Print spaces between brackets.", - oppositeDescription: "Do not print spaces between brackets." - }, +const options = { color: { // The supports-color package (a sub sub dependency) looks directly at // `process.argv` for `--no-color` and such-like options. The reason it is @@ -145,16 +116,6 @@ const detailedOptions = normalizeDetailedOptions({ description: "Define in which order config files and CLI options should be evaluated." }, - "cursor-offset": { - type: "int", - category: CATEGORY_EDITOR, - exception: -1, - forwardToApi: true, - description: dedent` - Print (to stderr) where a cursor at the given position would move to after formatting. - This option cannot be used with --range-start and --range-end. - ` - }, "debug-check": { type: "boolean" }, @@ -175,12 +136,6 @@ const detailedOptions = normalizeDetailedOptions({ description: "Find and print the path to a configuration file for the given input file." }, - "flow-parser": { - // Deprecated in 0.0.10 - type: "boolean", - category: CATEGORY_FORMAT, - deprecated: "Use `--parser flow` instead." - }, help: { type: "flag", alias: "h", @@ -195,19 +150,6 @@ const detailedOptions = normalizeDetailedOptions({ default: ".prettierignore", description: "Path to a file with patterns describing files to ignore." }, - "insert-pragma": { - type: "boolean", - forwardToApi: true, - description: dedent` - Insert @format pragma into file's first docblock comment. - ` - }, - "jsx-bracket-same-line": { - type: "boolean", - category: CATEGORY_FORMAT, - forwardToApi: true, - description: "Put > on the last line instead of at a new line." - }, "list-different": { type: "boolean", category: CATEGORY_OUTPUT, @@ -221,145 +163,14 @@ const detailedOptions = normalizeDetailedOptions({ default: "log", choices: ["silent", "error", "warn", "log", "debug"] }, - parser: { - type: "choice", - category: CATEGORY_FORMAT, - forwardToApi: true, - exception: value => typeof value === "string", // Allow path to a parser module. - choices: [ - "flow", - "babylon", - "typescript", - "css", - { value: "postcss", deprecated: true, redirect: "css" }, - "less", - "scss", - "json", - // "glimmer", - "graphql", - "markdown", - "vue" - ], - description: "Which parser to use.", - getter: (value, argv) => (argv["flow-parser"] ? "flow" : value) - }, - plugin: { - type: "path", - category: CATEGORY_CONFIG, - description: - "Add a plugin. Multiple plugins can be passed as separate `--plugin`s.", - forwardToApi: "plugins", - array: true - }, - "print-width": { - type: "int", - category: CATEGORY_FORMAT, - forwardToApi: true, - description: "The line length where Prettier will try wrap." - }, - "prose-wrap": { - type: "choice", - category: CATEGORY_FORMAT, - forwardToApi: true, - description: "How to wrap prose. (markdown)", - choices: [ - { - value: "always", - description: "Wrap prose if it exceeds the print width." - }, - { value: "never", description: "Do not wrap prose." }, - { value: "preserve", description: "Wrap prose as-is." }, - { value: false, deprecated: true, redirect: "never" } - ] - }, - "range-end": { - type: "int", - category: CATEGORY_EDITOR, - forwardToApi: true, - exception: Infinity, - description: dedent` - Format code ending at a given character offset (exclusive). - The range will extend forwards to the end of the selected statement. - This option cannot be used with --cursor-offset. - ` - }, - "range-start": { - type: "int", - category: CATEGORY_EDITOR, - forwardToApi: true, - description: dedent` - Format code starting at a given character offset. - The range will extend backwards to the start of the first line containing the selected statement. - This option cannot be used with --cursor-offset. - ` - }, - "require-pragma": { - type: "boolean", - forwardToApi: true, - description: dedent` - Require either '@prettier' or '@format' to be present in the file's first docblock comment - in order for it to be formatted. - ` - }, - semi: { - type: "boolean", - category: CATEGORY_FORMAT, - forwardToApi: true, - description: "Print semicolons.", - oppositeDescription: - "Do not print semicolons, except at the beginning of lines which may need them." - }, - "single-quote": { - type: "boolean", - category: CATEGORY_FORMAT, - forwardToApi: true, - description: "Use single quotes instead of double quotes." - }, stdin: { type: "boolean", description: "Force reading input from stdin." }, - "stdin-filepath": { - type: "path", - forwardToApi: "filepath", - description: "Path to the file to pretend that stdin comes from." - }, "support-info": { type: "boolean", description: "Print support information as JSON." }, - "tab-width": { - type: "int", - category: CATEGORY_FORMAT, - forwardToApi: true, - description: "Number of spaces per indentation level." - }, - "trailing-comma": { - type: "choice", - category: CATEGORY_FORMAT, - forwardToApi: true, - choices: [ - { value: "none", description: "No trailing commas." }, - { - value: "es5", - description: - "Trailing commas where valid in ES5 (objects, arrays, etc.)" - }, - { - value: "all", - description: - "Trailing commas wherever possible (including function arguments)." - }, - { value: "", deprecated: true, redirect: "es5" } - ], - description: "Print trailing commas wherever possible when multi-line." - }, - "use-tabs": { - type: "boolean", - category: CATEGORY_FORMAT, - forwardToApi: true, - description: "Indent with tabs instead of spaces." - }, version: { type: "boolean", alias: "v", @@ -375,29 +186,6 @@ const detailedOptions = normalizeDetailedOptions({ category: CATEGORY_OUTPUT, description: "Edit files in-place. (Beware!)" } -}); - -const minimistOptions = { - boolean: detailedOptions - .filter(option => option.type === "boolean") - .map(option => option.name), - string: detailedOptions - .filter(option => option.type !== "boolean") - .map(option => option.name), - default: detailedOptions - .filter(option => option.default !== undefined) - .reduce( - (current, option) => - Object.assign({ [option.name]: option.default }, current), - {} - ), - alias: detailedOptions - .filter(option => option.alias !== undefined) - .reduce( - (current, option) => - Object.assign({ [option.name]: option.alias }, current), - {} - ) }; const usageSummary = dedent` @@ -407,52 +195,13 @@ const usageSummary = dedent` Stdin is read if it is piped to Prettier and no files are given. `; -function normalizeDetailedOptions(rawDetailedOptions) { - const names = Object.keys(rawDetailedOptions).sort(); - - const normalized = names.map(name => { - const option = rawDetailedOptions[name]; - return Object.assign({}, option, { - name, - category: option.category || CATEGORY_OTHER, - forwardToApi: - option.forwardToApi && - (typeof option.forwardToApi === "string" - ? option.forwardToApi - : camelCase(name)), - choices: - option.choices && - option.choices.map(choice => - Object.assign( - { description: "", deprecated: false }, - typeof choice === "object" ? choice : { value: choice } - ) - ), - getter: option.getter || (value => value) - }); - }); - - return normalized; -} - -const detailedOptionMap = detailedOptions.reduce( - (current, option) => Object.assign(current, { [option.name]: option }), - {} -); - -const apiDetailedOptionMap = detailedOptions.reduce( - (current, option) => - option.forwardToApi && option.forwardToApi !== option.name - ? Object.assign(current, { [option.forwardToApi]: option }) - : current, - {} -); - module.exports = { + CATEGORY_CONFIG, + CATEGORY_EDITOR, + CATEGORY_FORMAT, + CATEGORY_OTHER, + CATEGORY_OUTPUT, categoryOrder, - minimistOptions, - detailedOptions, - detailedOptionMap, - apiDetailedOptionMap, + options, usageSummary }; diff --git a/src/cli/index.js b/src/cli/index.js index 3e17efcb..2ab8ac5d 100644 --- a/src/cli/index.js +++ b/src/cli/index.js @@ -1,62 +1,69 @@ "use strict"; -const minimist = require("minimist"); - -const prettier = eval("require")("../../index"); -const constant = require("./constant"); +const prettier = require("../../index"); +const stringify = require("json-stable-stringify"); const util = require("./util"); -const validator = require("./validator"); -const logger = require("./logger"); function run(args) { - const rawArgv = minimist(args, constant.minimistOptions); + const context = util.createContext(args); - process.env[logger.ENV_LOG_LEVEL] = - rawArgv["loglevel"] || constant.detailedOptionMap["loglevel"].default; + try { + util.initContext(context); - const argv = util.normalizeConfig("cli", rawArgv); + context.logger.debug(`normalized argv: ${JSON.stringify(context.argv)}`); - logger.debug(`normalized argv: ${JSON.stringify(argv)}`); + if (context.argv["write"] && context.argv["debug-check"]) { + context.logger.error("Cannot use --write and --debug-check together."); + process.exit(1); + } - argv.__args = args; - argv.__filePatterns = argv["_"]; + if (context.argv["find-config-path"] && context.filePatterns.length) { + context.logger.error("Cannot use --find-config-path with multiple files"); + process.exit(1); + } - validator.validateArgv(argv); + if (context.argv["version"]) { + context.logger.log(prettier.version); + process.exit(0); + } - if (argv["version"]) { - logger.log(prettier.version); - process.exit(0); - } + if (context.argv["help"] !== undefined) { + context.logger.log( + typeof context.argv["help"] === "string" && context.argv["help"] !== "" + ? util.createDetailedUsage(context, context.argv["help"]) + : util.createUsage(context) + ); + process.exit(0); + } - if (argv["help"] !== undefined) { - logger.log( - typeof argv["help"] === "string" && argv["help"] !== "" - ? util.createDetailedUsage(argv["help"]) - : util.createUsage() - ); - process.exit(0); - } + if (context.argv["support-info"]) { + context.logger.log( + prettier.format(stringify(prettier.getSupportInfo()), { + parser: "json" + }) + ); + process.exit(0); + } - if (argv["support-info"]) { - logger.log( - prettier.format(JSON.stringify(prettier.getSupportInfo()), { - parser: "json" - }) - ); - process.exit(0); - } + const hasFilePatterns = context.filePatterns.length !== 0; + const useStdin = + context.argv["stdin"] || (!hasFilePatterns && !process.stdin.isTTY); - const hasFilePatterns = argv.__filePatterns.length !== 0; - const useStdin = argv["stdin"] || (!hasFilePatterns && !process.stdin.isTTY); - - if (argv["find-config-path"]) { - util.logResolvedConfigPathOrDie(argv["find-config-path"]); - } else if (useStdin) { - util.formatStdin(argv); - } else if (hasFilePatterns) { - util.formatFiles(argv); - } else { - logger.log(util.createUsage()); + if (context.argv["find-config-path"]) { + util.logResolvedConfigPathOrDie( + context, + context.argv["find-config-path"] + ); + } else if (useStdin) { + util.formatStdin(context); + } else if (hasFilePatterns) { + util.formatFiles(context); + } else { + context.logger.log(util.createUsage(context)); + process.exit(1); + } + } catch (error) { + context.logger.error(error.message); process.exit(1); } } diff --git a/src/cli/logger.js b/src/cli/logger.js deleted file mode 100644 index 67646043..00000000 --- a/src/cli/logger.js +++ /dev/null @@ -1,57 +0,0 @@ -"use strict"; - -const ENV_LOG_LEVEL = "PRETTIER_LOG_LEVEL"; - -const chalk = require("chalk"); - -const warn = createLogger("warn", "yellow"); -const error = createLogger("error", "red"); -const debug = createLogger("debug", "blue"); -const log = createLogger("log"); - -function createLogger(loggerName, color) { - const prefix = color ? `[${chalk[color](loggerName)}] ` : ""; - return function(message, opts) { - opts = Object.assign({ newline: true }, opts); - if (shouldLog(loggerName)) { - const stream = process[loggerName === "log" ? "stdout" : "stderr"]; - stream.write(message.replace(/^/gm, prefix) + (opts.newline ? "\n" : "")); - } - }; -} - -function shouldLog(loggerName) { - const logLevel = process.env[ENV_LOG_LEVEL]; - - switch (logLevel) { - case "silent": - return false; - default: - return true; - case "debug": - if (loggerName === "debug") { - return true; - } - // fall through - case "log": - if (loggerName === "log") { - return true; - } - // fall through - case "warn": - if (loggerName === "warn") { - return true; - } - // fall through - case "error": - return loggerName === "error"; - } -} - -module.exports = { - warn, - error, - debug, - log, - ENV_LOG_LEVEL -}; diff --git a/src/cli/minimist.js b/src/cli/minimist.js new file mode 100644 index 00000000..aa62b68a --- /dev/null +++ b/src/cli/minimist.js @@ -0,0 +1,35 @@ +"use strict"; + +const minimist = require("minimist"); + +const PLACEHOLDER = null; + +/** + * unspecified boolean flag without default value is parsed as `undefined` instead of `false` + */ +module.exports = function(args, options) { + const boolean = options.boolean || []; + const defaults = options.default || {}; + + const booleanWithoutDefault = boolean.filter(key => !(key in defaults)); + const newDefaults = Object.assign( + {}, + defaults, + booleanWithoutDefault.reduce( + (reduced, key) => Object.assign(reduced, { [key]: PLACEHOLDER }), + {} + ) + ); + + const parsed = minimist( + args, + Object.assign({}, options, { default: newDefaults }) + ); + + return Object.keys(parsed).reduce((reduced, key) => { + if (parsed[key] !== PLACEHOLDER) { + reduced[key] = parsed[key]; + } + return reduced; + }, {}); +}; diff --git a/src/cli/util.js b/src/cli/util.js index 1ac7f526..15f86b3d 100644 --- a/src/cli/util.js +++ b/src/cli/util.js @@ -3,7 +3,6 @@ const path = require("path"); const camelCase = require("camelcase"); const dashify = require("dashify"); -const minimist = require("minimist"); const fs = require("fs"); const globby = require("globby"); const ignore = require("ignore"); @@ -11,37 +10,35 @@ const chalk = require("chalk"); const readline = require("readline"); const leven = require("leven"); -const prettier = eval("require")("../../index"); +const minimist = require("./minimist"); +const prettier = require("../../index"); const cleanAST = require("../common/clean-ast").cleanAST; const errors = require("../common/errors"); const resolver = require("../config/resolve-config"); const constant = require("./constant"); -const validator = require("./validator"); -const options = require("../main/options"); -const apiDefaultOptions = options.defaults; -const normalizeOptions = options.normalize; -const logger = require("./logger"); +const optionsModule = require("../main/options"); +const optionsNormalizer = require("../main/options-normalizer"); const thirdParty = require("../common/third-party"); +const getSupportInfo = require("../common/support").getSupportInfo; +const util = require("../common/util"); const OPTION_USAGE_THRESHOLD = 25; const CHOICE_USAGE_MARGIN = 3; const CHOICE_USAGE_INDENTATION = 2; -function getOptions(argv) { - return constant.detailedOptions.filter(option => option.forwardToApi).reduce( +function getOptions(argv, detailedOptions) { + return detailedOptions.filter(option => option.forwardToApi).reduce( (current, option) => Object.assign(current, { - [option.forwardToApi]: option.array - ? [].concat(argv[option.name] || []) - : argv[option.name] + [option.forwardToApi]: argv[option.name] }), {} ); } -function cliifyOptions(object) { +function cliifyOptions(object, apiDetailedOptionMap) { return Object.keys(object || {}).reduce((output, key) => { - const apiOption = constant.apiDetailedOptionMap[key]; + const apiOption = apiDetailedOptionMap[key]; const cliKey = apiOption ? apiOption.name : key; output[dashify(cliKey)] = object[key]; @@ -55,7 +52,7 @@ function diff(a, b) { }); } -function handleError(filename, error) { +function handleError(context, filename, error) { const isParseError = Boolean(error && error.loc); const isValidationError = /Validation Error/.test(error && error.message); @@ -66,25 +63,25 @@ function handleError(filename, error) { // `util.inspect` of throws things that aren't `Error` objects. (The Flow // parser has mistakenly thrown arrays sometimes.) if (isParseError) { - logger.error(`${filename}: ${String(error)}`); + context.logger.error(`${filename}: ${String(error)}`); } else if (isValidationError || error instanceof errors.ConfigError) { - logger.error(String(error)); + context.logger.error(String(error)); // If validation fails for one file, it will fail for all of them. process.exit(1); } else if (error instanceof errors.DebugError) { - logger.error(`${filename}: ${error.message}`); + context.logger.error(`${filename}: ${error.message}`); } else { - logger.error(filename + ": " + (error.stack || error)); + context.logger.error(filename + ": " + (error.stack || error)); } // Don't exit the process if one file failed process.exitCode = 2; } -function logResolvedConfigPathOrDie(filePath) { +function logResolvedConfigPathOrDie(context, filePath) { const configFile = resolver.resolveConfigFile.sync(filePath); if (configFile) { - logger.log(path.relative(process.cwd(), configFile)); + context.logger.log(path.relative(process.cwd(), configFile)); } else { process.exit(1); } @@ -99,16 +96,16 @@ function writeOutput(result, options) { } } -function listDifferent(argv, input, options, filename) { - if (!argv["list-different"]) { +function listDifferent(context, input, options, filename) { + if (!context.argv["list-different"]) { return; } options = Object.assign({}, options, { filepath: filename }); if (!prettier.check(input, options)) { - if (!argv["write"]) { - logger.log(filename); + if (!context.argv["write"]) { + context.logger.log(filename); } process.exitCode = 1; } @@ -116,13 +113,13 @@ function listDifferent(argv, input, options, filename) { return true; } -function format(argv, input, opt) { - if (argv["debug-print-doc"]) { +function format(context, input, opt) { + if (context.argv["debug-print-doc"]) { const doc = prettier.__debug.printToDoc(input, opt); return { formatted: prettier.__debug.formatDoc(doc) }; } - if (argv["debug-check"]) { + if (context.argv["debug-check"]) { const pp = prettier.format(input, opt); const pppp = prettier.format(pp, opt); if (pp !== pppp) { @@ -130,9 +127,15 @@ function format(argv, input, opt) { "prettier(input) !== prettier(prettier(input))\n" + diff(pp, pppp) ); } else { - const normalizedOpts = normalizeOptions(opt); - const ast = cleanAST(prettier.__debug.parse(input, opt), normalizedOpts); - const past = cleanAST(prettier.__debug.parse(pp, opt), normalizedOpts); + const normalizedOpts = optionsModule.normalize(opt); + const ast = cleanAST( + prettier.__debug.parse(input, opt).ast, + normalizedOpts + ); + const past = cleanAST( + prettier.__debug.parse(pp, opt).ast, + normalizedOpts + ); if (ast !== past) { const MAX_AST_SIZE = 2097152; // 2MB @@ -154,122 +157,142 @@ function format(argv, input, opt) { return prettier.formatWithCursor(input, opt); } -function getOptionsOrDie(argv, filePath) { +function getOptionsOrDie(context, filePath) { try { - if (argv["config"] === false) { - logger.debug("'--no-config' option found, skip loading config file."); + if (context.argv["config"] === false) { + context.logger.debug( + "'--no-config' option found, skip loading config file." + ); return null; } - logger.debug( - argv["config"] - ? `load config file from '${argv["config"]}'` + context.logger.debug( + context.argv["config"] + ? `load config file from '${context.argv["config"]}'` : `resolve config from '${filePath}'` ); + const options = resolver.resolveConfig.sync(filePath, { - editorconfig: argv.editorconfig, - config: argv["config"] + editorconfig: context.argv["editorconfig"], + config: context.argv["config"] }); - logger.debug("loaded options `" + JSON.stringify(options) + "`"); + context.logger.debug("loaded options `" + JSON.stringify(options) + "`"); return options; } catch (error) { - logger.error("Invalid configuration file: " + error.message); + context.logger.error("Invalid configuration file: " + error.message); process.exit(2); } } -function getOptionsForFile(argv, filepath) { - const options = getOptionsOrDie(argv, filepath); +function getOptionsForFile(context, filepath) { + const options = getOptionsOrDie(context, filepath); + + const hasPlugins = options && options.plugins; + if (hasPlugins) { + pushContextPlugins(context, options.plugins); + } const appliedOptions = Object.assign( { filepath }, applyConfigPrecedence( - argv, - options && normalizeConfig("api", options, constant.detailedOptionMap) + context, + options && + optionsNormalizer.normalizeApiOptions(options, context.supportOptions, { + logger: context.logger + }) ) ); - logger.debug( - `applied config-precedence (${argv["config-precedence"]}): ` + + context.logger.debug( + `applied config-precedence (${context.argv["config-precedence"]}): ` + `${JSON.stringify(appliedOptions)}` ); + + if (hasPlugins) { + popContextPlugins(context); + } + return appliedOptions; } -function parseArgsToOptions(argv, overrideDefaults) { +function parseArgsToOptions(context, overrideDefaults) { + const minimistOptions = createMinimistOptions(context.detailedOptions); + const apiDetailedOptionMap = createApiDetailedOptionMap( + context.detailedOptions + ); return getOptions( - normalizeConfig( - "cli", + optionsNormalizer.normalizeCliOptions( minimist( - argv.__args, + context.args, Object.assign({ - string: constant.minimistOptions.string, - boolean: constant.minimistOptions.boolean, - default: Object.assign( - {}, - cliifyOptions(apiDefaultOptions), - cliifyOptions(overrideDefaults) - ) + string: minimistOptions.string, + boolean: minimistOptions.boolean, + default: cliifyOptions(overrideDefaults, apiDetailedOptionMap) }) ), - { warning: false } - ) + context.detailedOptions, + { logger: false } + ), + context.detailedOptions ); } -function applyConfigPrecedence(argv, options) { +function applyConfigPrecedence(context, options) { try { - switch (argv["config-precedence"]) { + switch (context.argv["config-precedence"]) { case "cli-override": - return parseArgsToOptions(argv, options); + return parseArgsToOptions(context, options); case "file-override": - return Object.assign({}, parseArgsToOptions(argv), options); + return Object.assign({}, parseArgsToOptions(context), options); case "prefer-file": - return options || parseArgsToOptions(argv); + return options || parseArgsToOptions(context); } } catch (error) { - logger.error(error.toString()); + context.logger.error(error.toString()); process.exit(2); } } -function formatStdin(argv) { - const filepath = argv["stdin-filepath"] - ? path.resolve(process.cwd(), argv["stdin-filepath"]) +function formatStdin(context) { + const filepath = context.argv["stdin-filepath"] + ? path.resolve(process.cwd(), context.argv["stdin-filepath"]) : process.cwd(); - const ignorer = createIgnorer(argv); + const ignorer = createIgnorer(context); const relativeFilepath = path.relative(process.cwd(), filepath); - if (relativeFilepath && ignorer.filter([relativeFilepath]).length === 0) { - return; - } - thirdParty.getStream(process.stdin).then(input => { - const options = getOptionsForFile(argv, filepath); + if (relativeFilepath && ignorer.filter([relativeFilepath]).length === 0) { + writeOutput({ formatted: input }, {}); + return; + } - if (listDifferent(argv, input, options, "(stdin)")) { + const options = getOptionsForFile(context, filepath); + + if (listDifferent(context, input, options, "(stdin)")) { return; } try { - writeOutput(format(argv, input, options), options); + writeOutput(format(context, input, options), options); } catch (error) { - handleError("stdin", error); + handleError(context, "stdin", error); } }); } -function createIgnorer(argv) { - const ignoreFilePath = path.resolve(argv["ignore-path"]); +function createIgnorer(context) { + const ignoreFilePath = path.resolve(context.argv["ignore-path"]); let ignoreText = ""; try { ignoreText = fs.readFileSync(ignoreFilePath, "utf8"); } catch (readError) { if (readError.code !== "ENOENT") { - logger.error(`Unable to read ${ignoreFilePath}: ` + readError.message); + context.logger.error( + `Unable to read ${ignoreFilePath}: ` + readError.message + ); process.exit(2); } } @@ -277,12 +300,8 @@ function createIgnorer(argv) { return ignore().add(ignoreText); } -function eachFilename(argv, patterns, callback) { - const ignoreNodeModules = argv["with-node-modules"] === false; - // The ignorer will be used to filter file paths after the glob is checked, - // before any files are actually read - const ignorer = createIgnorer(argv); - +function eachFilename(context, patterns, callback) { + const ignoreNodeModules = context.argv["with-node-modules"] !== true; if (ignoreNodeModules) { patterns = patterns.concat(["!**/node_modules/**", "!./node_modules/**"]); } @@ -293,17 +312,17 @@ function eachFilename(argv, patterns, callback) { .map(filePath => path.relative(process.cwd(), filePath)); if (filePaths.length === 0) { - logger.error(`No matching files. Patterns tried: ${patterns.join(" ")}`); + context.logger.error( + `No matching files. Patterns tried: ${patterns.join(" ")}` + ); process.exitCode = 2; return; } - ignorer - .filter(filePaths) - .forEach(filePath => - callback(filePath, getOptionsForFile(argv, filePath)) - ); + filePaths.forEach(filePath => + callback(filePath, getOptionsForFile(context, filePath)) + ); } catch (error) { - logger.error( + context.logger.error( `Unable to expand glob patterns: ${patterns.join(" ")}\n${error.message}` ); // Don't exit the process if one pattern failed @@ -311,11 +330,25 @@ function eachFilename(argv, patterns, callback) { } } -function formatFiles(argv) { - eachFilename(argv, argv.__filePatterns, (filename, options) => { - if (argv["write"] && process.stdout.isTTY) { +function formatFiles(context) { + // The ignorer will be used to filter file paths after the glob is checked, + // before any files are actually written + const ignorer = createIgnorer(context); + + eachFilename(context, context.filePatterns, (filename, options) => { + const fileIgnored = ignorer.filter([filename]).length === 0; + if ( + fileIgnored && + (context.argv["debug-check"] || + context.argv["write"] || + context.argv["list-different"]) + ) { + return; + } + + if (context.argv["write"] && process.stdout.isTTY) { // Don't use `console.log` here since we need to replace this line. - logger.log(filename, { newline: false }); + context.logger.log(filename, { newline: false }); } let input; @@ -323,15 +356,22 @@ function formatFiles(argv) { input = fs.readFileSync(filename, "utf8"); } catch (error) { // Add newline to split errors from filename line. - logger.log(""); + context.logger.log(""); - logger.error(`Unable to read file: ${filename}\n${error.message}`); + context.logger.error( + `Unable to read file: ${filename}\n${error.message}` + ); // Don't exit the process if one file failed process.exitCode = 2; return; } - listDifferent(argv, input, options, filename); + if (fileIgnored) { + writeOutput({ formatted: input }, options); + return; + } + + listDifferent(context, input, options, filename); const start = Date.now(); @@ -340,7 +380,7 @@ function formatFiles(argv) { try { result = format( - argv, + context, input, Object.assign({}, options, { filepath: filename }) ); @@ -349,11 +389,11 @@ function formatFiles(argv) { // Add newline to split errors from filename line. process.stdout.write("\n"); - handleError(filename, error); + handleError(context, filename, error); return; } - if (argv["write"]) { + if (context.argv["write"]) { if (process.stdout.isTTY) { // Remove previously printed filename to log it with duration. readline.clearLine(process.stdout, 0); @@ -363,31 +403,33 @@ function formatFiles(argv) { // Don't write the file if it won't change in order not to invalidate // mtime based caches. if (output === input) { - if (!argv["list-different"]) { - logger.log(`${chalk.grey(filename)} ${Date.now() - start}ms`); + if (!context.argv["list-different"]) { + context.logger.log(`${chalk.grey(filename)} ${Date.now() - start}ms`); } } else { - if (argv["list-different"]) { - logger.log(filename); + if (context.argv["list-different"]) { + context.logger.log(filename); } else { - logger.log(`${filename} ${Date.now() - start}ms`); + context.logger.log(`${filename} ${Date.now() - start}ms`); } try { fs.writeFileSync(filename, output, "utf8"); } catch (error) { - logger.error(`Unable to write file: ${filename}\n${error.message}`); + context.logger.error( + `Unable to write file: ${filename}\n${error.message}` + ); // Don't exit the process if one file failed process.exitCode = 2; } } - } else if (argv["debug-check"]) { + } else if (context.argv["debug-check"]) { if (output) { - logger.log(output); + context.logger.log(output); } else { process.exitCode = 2; } - } else if (!argv["list-different"]) { + } else if (!context.argv["list-different"]) { writeOutput(result, options); } }); @@ -408,8 +450,8 @@ function getOptionsWithOpposites(options) { return flattenArray(optionsWithOpposites).filter(Boolean); } -function createUsage() { - const options = getOptionsWithOpposites(constant.detailedOptions).filter( +function createUsage(context) { + const options = getOptionsWithOpposites(context.detailedOptions).filter( // remove unnecessary option (e.g. `semi`, `color`, etc.), which is only used for --help option => !( @@ -432,7 +474,7 @@ function createUsage() { const optionsUsage = allCategories.map(category => { const categoryOptions = groupedOptions[category] - .map(option => createOptionUsage(option, OPTION_USAGE_THRESHOLD)) + .map(option => createOptionUsage(context, option, OPTION_USAGE_THRESHOLD)) .join("\n"); return `${category} options:\n\n${indent(categoryOptions, 2)}`; }); @@ -440,20 +482,26 @@ function createUsage() { return [constant.usageSummary].concat(optionsUsage, [""]).join("\n\n"); } -function createOptionUsage(option, threshold) { +function createOptionUsage(context, option, threshold) { const header = createOptionUsageHeader(option); - const optionDefaultValue = getOptionDefaultValue(option.name); + const optionDefaultValue = getOptionDefaultValue(context, option.name); return createOptionUsageRow( header, `${option.description}${ optionDefaultValue === undefined ? "" - : `\nDefaults to ${optionDefaultValue}.` + : `\nDefaults to ${createDefaultValueDisplay(optionDefaultValue)}.` }`, threshold ); } +function createDefaultValueDisplay(value) { + return Array.isArray(value) + ? `[${value.map(createDefaultValueDisplay).join(", ")}]` + : value; +} + function createOptionUsageHeader(option) { const name = `--${option.name}`; const alias = option.alias ? `-${option.alias},` : null; @@ -490,7 +538,7 @@ function flattenArray(array) { return [].concat.apply([], array); } -function getOptionWithLevenSuggestion(options, optionName) { +function getOptionWithLevenSuggestion(context, options, optionName) { // support aliases const optionNameContainers = flattenArray( options.map((option, index) => [ @@ -513,14 +561,14 @@ function getOptionWithLevenSuggestion(options, optionName) { if (suggestedOptionNameContainer !== undefined) { const suggestedOptionName = suggestedOptionNameContainer.value; - logger.warn( + context.logger.warn( `Unknown option name "${optionName}", did you mean "${suggestedOptionName}"?` ); return options[suggestedOptionNameContainer.index]; } - logger.warn(`Unknown option name "${optionName}"`); + context.logger.warn(`Unknown option name "${optionName}"`); return options.find(option => option.name === "help"); } @@ -538,9 +586,10 @@ function createChoiceUsages(choices, margin, indentation) { ); } -function createDetailedUsage(optionName) { +function createDetailedUsage(context, optionName) { const option = getOptionWithLevenSuggestion( - getOptionsWithOpposites(constant.detailedOptions), + context, + getOptionsWithOpposites(context.detailedOptions), optionName ); @@ -556,30 +605,39 @@ function createDetailedUsage(optionName) { CHOICE_USAGE_INDENTATION ).join("\n")}`; - const optionDefaultValue = getOptionDefaultValue(option.name); + const optionDefaultValue = getOptionDefaultValue(context, option.name); const defaults = optionDefaultValue !== undefined - ? `\n\nDefault: ${optionDefaultValue}` + ? `\n\nDefault: ${createDefaultValueDisplay(optionDefaultValue)}` : ""; - return `${header}${description}${choices}${defaults}`; + const pluginDefaults = + option.pluginDefaults && Object.keys(option.pluginDefaults).length + ? `\nPlugin defaults:${Object.keys(option.pluginDefaults).map( + key => + `\n* ${key}: ${createDefaultValueDisplay( + option.pluginDefaults[key] + )}` + )}` + : ""; + return `${header}${description}${choices}${defaults}${pluginDefaults}`; } -function getOptionDefaultValue(optionName) { +function getOptionDefaultValue(context, optionName) { // --no-option - if (!(optionName in constant.detailedOptionMap)) { + if (!(optionName in context.detailedOptionMap)) { return undefined; } - const option = constant.detailedOptionMap[optionName]; + const option = context.detailedOptionMap[optionName]; if (option.default !== undefined) { return option.default; } const optionCamelName = camelCase(optionName); - if (optionCamelName in apiDefaultOptions) { - return apiDefaultOptions[optionCamelName]; + if (optionCamelName in context.apiDefaultOptions) { + return context.apiDefaultOptions[optionCamelName]; } return undefined; @@ -597,135 +655,254 @@ function groupBy(array, getKey) { }, Object.create(null)); } -/** @param {'api' | 'cli'} type */ -function normalizeConfig(type, rawConfig, options) { - if (type === "api" && rawConfig === null) { - return null; +function pick(object, keys) { + return !keys + ? object + : keys.reduce( + (reduced, key) => Object.assign(reduced, { [key]: object[key] }), + {} + ); +} + +function createLogger(logLevel) { + return { + warn: createLogFunc("warn", "yellow"), + error: createLogFunc("error", "red"), + debug: createLogFunc("debug", "blue"), + log: createLogFunc("log") + }; + + function createLogFunc(loggerName, color) { + if (!shouldLog(loggerName)) { + return () => {}; + } + + const prefix = color ? `[${chalk[color](loggerName)}] ` : ""; + return function(message, opts) { + opts = Object.assign({ newline: true }, opts); + const stream = process[loggerName === "log" ? "stdout" : "stderr"]; + stream.write(message.replace(/^/gm, prefix) + (opts.newline ? "\n" : "")); + }; } - options = options || {}; - - const consoleWarn = - options.warning === false ? () => {} : logger.warn.bind(logger); - - const normalized = {}; - - Object.keys(rawConfig).forEach(rawKey => { - const rawValue = rawConfig[rawKey]; - - const key = type === "cli" ? rawKey : dashify(rawKey); - - if (type === "cli" && key === "_") { - normalized[rawKey] = rawValue; - return; - } - - if (type === "cli" && key.length === 1) { - // do nothing with alias - return; - } - - let option = constant.detailedOptionMap[key]; - if (type === "api" && option === undefined) { - option = constant.apiDetailedOptionMap[key]; - } - - // unknown option - if (option === undefined) { - if (type === "api") { - consoleWarn(`Ignored unknown option: ${rawKey}`); - } else { - const optionName = rawValue === false ? `no-${rawKey}` : rawKey; - consoleWarn(`Ignored unknown option: --${optionName}`); - } - return; - } - - const value = getValue(rawValue, option); - - if (option.exception !== undefined) { - if (typeof option.exception === "function") { - if (option.exception(value)) { - normalized[rawKey] = value; - return; + function shouldLog(loggerName) { + switch (logLevel) { + case "silent": + return false; + default: + return true; + case "debug": + if (loggerName === "debug") { + return true; } - } else { - if (value === option.exception) { - normalized[rawKey] = value; - return; + // fall through + case "log": + if (loggerName === "log") { + return true; } - } + // fall through + case "warn": + if (loggerName === "warn") { + return true; + } + // fall through + case "error": + return loggerName === "error"; } - - try { - switch (option.type) { - case "int": - validator.validateIntOption(type, value, option); - normalized[rawKey] = Number(value); - break; - case "choice": - validator.validateChoiceOption(type, value, option); - normalized[rawKey] = value; - break; - default: - normalized[rawKey] = value; - break; - } - } catch (error) { - logger.error(error.message); - process.exit(2); - } - }); - - return normalized; - - function getOptionName(option) { - return type === "cli" ? `--${option.name}` : camelCase(option.name); - } - - function getRedirectName(option, choice) { - return type === "cli" - ? `--${option.name}=${choice.redirect}` - : `{ ${camelCase(option.name)}: ${JSON.stringify(choice.redirect)} }`; - } - - function getValue(rawValue, option) { - const optionName = getOptionName(option); - if (rawValue && option.deprecated) { - let warning = `\`${optionName}\` is deprecated.`; - if (typeof option.deprecated === "string") { - warning += ` ${option.deprecated}`; - } - consoleWarn(warning); - } - - const value = option.getter(rawValue, rawConfig); - - if (option.type === "choice") { - const choice = option.choices.find(choice => choice.value === rawValue); - if (choice !== undefined && choice.deprecated) { - const warningDescription = - rawValue === "" - ? "without an argument" - : `with value \`${rawValue}\``; - const redirectName = getRedirectName(option, choice); - consoleWarn( - `\`${optionName}\` ${warningDescription} is deprecated. Prettier now treats it as: \`${redirectName}\`.` - ); - return choice.redirect; - } - } - - return value; } } +function normalizeDetailedOption(name, option) { + return Object.assign({ category: constant.CATEGORY_OTHER }, option, { + choices: + option.choices && + option.choices.map(choice => { + const newChoice = Object.assign( + { description: "", deprecated: false }, + typeof choice === "object" ? choice : { value: choice } + ); + if (newChoice.value === true) { + newChoice.value = ""; // backward compability for original boolean option + } + return newChoice; + }) + }); +} + +function normalizeDetailedOptionMap(detailedOptionMap) { + return Object.keys(detailedOptionMap) + .sort() + .reduce((normalized, name) => { + const option = detailedOptionMap[name]; + return Object.assign(normalized, { + [name]: normalizeDetailedOption(name, option) + }); + }, {}); +} + +function createMinimistOptions(detailedOptions) { + return { + boolean: detailedOptions + .filter(option => option.type === "boolean") + .map(option => option.name), + string: detailedOptions + .filter(option => option.type !== "boolean") + .map(option => option.name), + default: detailedOptions + .filter(option => !option.deprecated) + .filter(option => !option.forwardToApi || option.name === "plugin") + .filter(option => option.default !== undefined) + .reduce( + (current, option) => + Object.assign({ [option.name]: option.default }, current), + {} + ), + alias: detailedOptions + .filter(option => option.alias !== undefined) + .reduce( + (current, option) => + Object.assign({ [option.name]: option.alias }, current), + {} + ) + }; +} + +function createApiDetailedOptionMap(detailedOptions) { + return detailedOptions.reduce( + (current, option) => + option.forwardToApi && option.forwardToApi !== option.name + ? Object.assign(current, { [option.forwardToApi]: option }) + : current, + {} + ); +} + +function createDetailedOptionMap(supportOptions) { + return supportOptions.reduce((reduced, option) => { + const newOption = Object.assign({}, option, { + name: option.cliName || dashify(option.name), + description: option.cliDescription || option.description, + category: option.cliCategory || constant.CATEGORY_FORMAT, + forwardToApi: option.name + }); + + if (option.deprecated) { + delete newOption.forwardToApi; + delete newOption.description; + delete newOption.oppositeDescription; + newOption.deprecated = true; + } + + return Object.assign(reduced, { [newOption.name]: newOption }); + }, {}); +} + +//-----------------------------context-util-start------------------------------- +/** + * @typedef {Object} Context + * @property logger + * @property args + * @property argv + * @property filePatterns + * @property supportOptions + * @property detailedOptions + * @property detailedOptionMap + * @property apiDefaultOptions + */ +function createContext(args) { + const context = { args }; + + updateContextArgv(context); + normalizeContextArgv(context, ["loglevel", "plugin"]); + + context.logger = createLogger(context.argv["loglevel"]); + + updateContextArgv(context, context.argv["plugin"]); + + return context; +} + +function initContext(context) { + // split into 2 step so that we could wrap this in a `try..catch` in cli/index.js + normalizeContextArgv(context); +} + +function updateContextOptions(context, plugins) { + const supportOptions = getSupportInfo(null, { + showDeprecated: true, + showUnreleased: true, + showInternal: true, + plugins + }).options; + + const detailedOptionMap = normalizeDetailedOptionMap( + Object.assign({}, createDetailedOptionMap(supportOptions), constant.options) + ); + + const detailedOptions = util.arrayify(detailedOptionMap, "name"); + + const apiDefaultOptions = supportOptions + .filter(optionInfo => !optionInfo.deprecated) + .reduce( + (reduced, optionInfo) => + Object.assign(reduced, { [optionInfo.name]: optionInfo.default }), + Object.assign({}, optionsModule.hiddenDefaults) + ); + + context.supportOptions = supportOptions; + context.detailedOptions = detailedOptions; + context.detailedOptionMap = detailedOptionMap; + context.apiDefaultOptions = apiDefaultOptions; +} + +function pushContextPlugins(context, plugins) { + context._supportOptions = context.supportOptions; + context._detailedOptions = context.detailedOptions; + context._detailedOptionMap = context.detailedOptionMap; + context._apiDefaultOptions = context.apiDefaultOptions; + updateContextOptions(context, plugins); +} + +function popContextPlugins(context) { + context.supportOptions = context._supportOptions; + context.detailedOptions = context._detailedOptions; + context.detailedOptionMap = context._detailedOptionMap; + context.apiDefaultOptions = context._apiDefaultOptions; +} + +function updateContextArgv(context, plugins) { + pushContextPlugins(context, plugins); + + const minimistOptions = createMinimistOptions(context.detailedOptions); + const argv = minimist(context.args, minimistOptions); + + context.argv = argv; + context.filePatterns = argv["_"]; +} + +function normalizeContextArgv(context, keys) { + const detailedOptions = !keys + ? context.detailedOptions + : context.detailedOptions.filter( + option => keys.indexOf(option.name) !== -1 + ); + const argv = !keys ? context.argv : pick(context.argv, keys); + + context.argv = optionsNormalizer.normalizeCliOptions(argv, detailedOptions, { + logger: context.logger + }); +} +//------------------------------context-util-end-------------------------------- + module.exports = { - logResolvedConfigPathOrDie, - format, - formatStdin, - formatFiles, - createUsage, + createContext, + createDetailedOptionMap, createDetailedUsage, - normalizeConfig + createUsage, + format, + formatFiles, + formatStdin, + initContext, + logResolvedConfigPathOrDie, + normalizeDetailedOptionMap }; diff --git a/src/cli/validator.js b/src/cli/validator.js deleted file mode 100644 index 72a496ea..00000000 --- a/src/cli/validator.js +++ /dev/null @@ -1,55 +0,0 @@ -"use strict"; - -const camelCase = require("camelcase"); -const logger = require("./logger"); - -function validateArgv(argv) { - if (argv["write"] && argv["debug-check"]) { - logger.error("Cannot use --write and --debug-check together."); - process.exit(1); - } - - if (argv["find-config-path"] && argv.__filePatterns.length) { - logger.error("Cannot use --find-config-path with multiple files"); - process.exit(1); - } -} - -function getOptionName(type, option) { - return type === "cli" ? `--${option.name}` : camelCase(option.name); -} - -function validateIntOption(type, value, option) { - if (!/^\d+$/.test(value) || (type === "api" && typeof value !== "number")) { - const optionName = getOptionName(type, option); - throw new Error( - `Invalid ${optionName} value.\n` + - `Expected an integer, but received: ${JSON.stringify(value)}` - ); - } -} - -function validateChoiceOption(type, value, option) { - if (!option.choices.some(choice => choice.value === value)) { - const optionName = getOptionName(type, option); - throw new Error( - `Invalid option for ${optionName}.\n` + - `Expected ${getJoinedChoices()}, but received: ${JSON.stringify(value)}` - ); - } - - function getJoinedChoices() { - const choices = option.choices - .filter(choice => !choice.deprecated) - .map(choice => `"${choice.value}"`); - const head = choices.slice(0, -2); - const tail = choices.slice(-2); - return head.concat(tail.join(" or ")).join(", "); - } -} - -module.exports = { - validateArgv, - validateIntOption, - validateChoiceOption -}; diff --git a/src/common/fast-path.js b/src/common/fast-path.js index 4c149001..eb678a9a 100644 --- a/src/common/fast-path.js +++ b/src/common/fast-path.js @@ -1,8 +1,6 @@ "use strict"; const assert = require("assert"); -const util = require("../common/util"); -const startsWithNoLookaheadToken = util.startsWithNoLookaheadToken; function FastPath(value) { assert.ok(this instanceof FastPath); @@ -131,541 +129,4 @@ FastPath.prototype.map = function map(callback /*, name1, name2, ... */) { return result; }; -FastPath.prototype.needsParens = function(options) { - const parent = this.getParentNode(); - if (!parent) { - return false; - } - - const name = this.getName(); - const node = this.getNode(); - - // If the value of this path is some child of a Node and not a Node - // itself, then it doesn't need parentheses. Only Node objects (in - // fact, only Expression nodes) need parentheses. - if (this.getValue() !== node) { - return false; - } - - // Only statements don't need parentheses. - if (isStatement(node)) { - return false; - } - - // Closure compiler requires that type casted expressions to be surrounded by - // parentheses. - if (util.hasClosureCompilerTypeCastComment(options.originalText, node)) { - return true; - } - - // Identifiers never need parentheses. - if (node.type === "Identifier") { - return false; - } - - if (parent.type === "ParenthesizedExpression") { - return false; - } - - // Add parens around the extends clause of a class. It is needed for almost - // all expressions. - if ( - (parent.type === "ClassDeclaration" || parent.type === "ClassExpression") && - parent.superClass === node && - (node.type === "ArrowFunctionExpression" || - node.type === "AssignmentExpression" || - node.type === "AwaitExpression" || - node.type === "BinaryExpression" || - node.type === "ConditionalExpression" || - node.type === "LogicalExpression" || - node.type === "NewExpression" || - node.type === "ObjectExpression" || - node.type === "ParenthesizedExpression" || - node.type === "SequenceExpression" || - node.type === "TaggedTemplateExpression" || - node.type === "UnaryExpression" || - node.type === "UpdateExpression" || - node.type === "YieldExpression") - ) { - return true; - } - - if ( - (parent.type === "ArrowFunctionExpression" && - parent.body === node && - node.type !== "SequenceExpression" && // these have parens added anyway - startsWithNoLookaheadToken(node, /* forbidFunctionAndClass */ false)) || - (parent.type === "ExpressionStatement" && - startsWithNoLookaheadToken(node, /* forbidFunctionAndClass */ true)) - ) { - return true; - } - - switch (node.type) { - case "CallExpression": { - let firstParentNotMemberExpression = parent; - let i = 0; - while ( - firstParentNotMemberExpression && - firstParentNotMemberExpression.type === "MemberExpression" - ) { - firstParentNotMemberExpression = this.getParentNode(++i); - } - - if ( - firstParentNotMemberExpression.type === "NewExpression" && - firstParentNotMemberExpression.callee === this.getParentNode(i - 1) - ) { - return true; - } - return false; - } - - case "SpreadElement": - case "SpreadProperty": - return ( - parent.type === "MemberExpression" && - name === "object" && - parent.object === node - ); - - case "UpdateExpression": - if (parent.type === "UnaryExpression") { - return ( - node.prefix && - ((node.operator === "++" && parent.operator === "+") || - (node.operator === "--" && parent.operator === "-")) - ); - } - // else fallthrough - case "UnaryExpression": - switch (parent.type) { - case "UnaryExpression": - return ( - node.operator === parent.operator && - (node.operator === "+" || node.operator === "-") - ); - - case "MemberExpression": - return name === "object" && parent.object === node; - - case "TaggedTemplateExpression": - return true; - - case "NewExpression": - case "CallExpression": - return name === "callee" && parent.callee === node; - - case "BinaryExpression": - return parent.operator === "**" && name === "left"; - - default: - return false; - } - - case "BinaryExpression": { - if (parent.type === "UpdateExpression") { - return true; - } - - const isLeftOfAForStatement = node => { - let i = 0; - while (node) { - const parent = this.getParentNode(i++); - if (!parent) { - return false; - } - if (parent.type === "ForStatement" && parent.init === node) { - return true; - } - node = parent; - } - return false; - }; - if (node.operator === "in" && isLeftOfAForStatement(node)) { - return true; - } - } - // fallthrough - case "TSTypeAssertionExpression": - case "TSAsExpression": - case "LogicalExpression": - switch (parent.type) { - case "ConditionalExpression": - return node.type === "TSAsExpression"; - - case "CallExpression": - case "NewExpression": - return name === "callee" && parent.callee === node; - - case "ClassDeclaration": - case "TSAbstractClassDeclaration": - return name === "superClass" && parent.superClass === node; - case "TSTypeAssertionExpression": - case "TaggedTemplateExpression": - case "UnaryExpression": - case "SpreadElement": - case "SpreadProperty": - case "ExperimentalSpreadProperty": - case "BindExpression": - case "AwaitExpression": - case "TSAsExpression": - case "TSNonNullExpression": - return true; - - case "MemberExpression": - return name === "object" && parent.object === node; - - case "AssignmentExpression": - return ( - parent.left === node && - (node.type === "TSTypeAssertionExpression" || - node.type === "TSAsExpression") - ); - case "Decorator": - return ( - parent.expression === node && - (node.type === "TSTypeAssertionExpression" || - node.type === "TSAsExpression") - ); - - case "BinaryExpression": - case "LogicalExpression": { - if (!node.operator && node.type !== "TSTypeAssertionExpression") { - return true; - } - - const po = parent.operator; - const pp = util.getPrecedence(po); - const no = node.operator; - const np = util.getPrecedence(no); - - if (pp > np) { - return true; - } - - if ((po === "||" || po === "??") && no === "&&") { - return true; - } - - if (pp === np && name === "right") { - assert.strictEqual(parent.right, node); - return true; - } - - if (pp === np && !util.shouldFlatten(po, no)) { - return true; - } - - // Add parenthesis when working with binary operators - // It's not stricly needed but helps with code understanding - if (util.isBitwiseOperator(po)) { - return true; - } - - return false; - } - - default: - return false; - } - - case "TSParenthesizedType": { - const grandParent = this.getParentNode(1); - if ( - (parent.type === "TSTypeParameter" || - parent.type === "TypeParameter" || - parent.type === "VariableDeclarator" || - parent.type === "TSTypeAnnotation" || - parent.type === "GenericTypeAnnotation" || - parent.type === "TSTypeReference") && - (node.typeAnnotation.type === "TSTypeAnnotation" && - node.typeAnnotation.typeAnnotation.type !== "TSFunctionType" && - grandParent.type !== "TSTypeOperator") - ) { - return false; - } - // Delegate to inner TSParenthesizedType - if (node.typeAnnotation.type === "TSParenthesizedType") { - return false; - } - return true; - } - - case "SequenceExpression": - switch (parent.type) { - case "ReturnStatement": - return false; - - case "ForStatement": - // Although parentheses wouldn't hurt around sequence - // expressions in the head of for loops, traditional style - // dictates that e.g. i++, j++ should not be wrapped with - // parentheses. - return false; - - case "ExpressionStatement": - return name !== "expression"; - - case "ArrowFunctionExpression": - // We do need parentheses, but SequenceExpressions are handled - // specially when printing bodies of arrow functions. - return name !== "body"; - - default: - // Otherwise err on the side of overparenthesization, adding - // explicit exceptions above if this proves overzealous. - return true; - } - - case "YieldExpression": - if ( - parent.type === "UnaryExpression" || - parent.type === "AwaitExpression" || - parent.type === "TSAsExpression" || - parent.type === "TSNonNullExpression" - ) { - return true; - } - // else fallthrough - case "AwaitExpression": - switch (parent.type) { - case "TaggedTemplateExpression": - case "BinaryExpression": - case "LogicalExpression": - case "SpreadElement": - case "SpreadProperty": - case "TSAsExpression": - case "TSNonNullExpression": - return true; - - case "MemberExpression": - return parent.object === node; - - case "NewExpression": - case "CallExpression": - return parent.callee === node; - - case "ConditionalExpression": - return parent.test === node; - - default: - return false; - } - - case "ArrayTypeAnnotation": - return parent.type === "NullableTypeAnnotation"; - - case "IntersectionTypeAnnotation": - case "UnionTypeAnnotation": - return ( - parent.type === "ArrayTypeAnnotation" || - parent.type === "NullableTypeAnnotation" || - parent.type === "IntersectionTypeAnnotation" || - parent.type === "UnionTypeAnnotation" - ); - - case "NullableTypeAnnotation": - return parent.type === "ArrayTypeAnnotation"; - - case "FunctionTypeAnnotation": - return ( - parent.type === "UnionTypeAnnotation" || - parent.type === "IntersectionTypeAnnotation" || - parent.type === "ArrayTypeAnnotation" - ); - - case "StringLiteral": - case "NumericLiteral": - case "Literal": - if ( - typeof node.value === "string" && - parent.type === "ExpressionStatement" && - // TypeScript workaround for eslint/typescript-eslint-parser#267 - // See corresponding workaround in printer.js case: "Literal" - ((options.parser !== "typescript" && !parent.directive) || - (options.parser === "typescript" && - options.originalText.substr(util.locStart(node) - 1, 1) === "(")) - ) { - // To avoid becoming a directive - const grandParent = this.getParentNode(1); - - return ( - grandParent.type === "Program" || - grandParent.type === "BlockStatement" - ); - } - - return ( - parent.type === "MemberExpression" && - typeof node.value === "number" && - name === "object" && - parent.object === node - ); - - case "AssignmentExpression": { - const grandParent = this.getParentNode(1); - - if (parent.type === "ArrowFunctionExpression" && parent.body === node) { - return true; - } else if ( - parent.type === "ClassProperty" && - parent.key === node && - parent.computed - ) { - return false; - } else if ( - parent.type === "TSPropertySignature" && - parent.name === node - ) { - return false; - } else if ( - parent.type === "ForStatement" && - (parent.init === node || parent.update === node) - ) { - return false; - } else if (parent.type === "ExpressionStatement") { - return node.left.type === "ObjectPattern"; - } else if (parent.type === "TSPropertySignature" && parent.key === node) { - return false; - } else if (parent.type === "AssignmentExpression") { - return false; - } else if ( - parent.type === "SequenceExpression" && - grandParent && - grandParent.type === "ForStatement" && - (grandParent.init === parent || grandParent.update === parent) - ) { - return false; - } - return true; - } - case "ConditionalExpression": - switch (parent.type) { - case "TaggedTemplateExpression": - case "UnaryExpression": - case "SpreadElement": - case "SpreadProperty": - case "BinaryExpression": - case "LogicalExpression": - case "ExportDefaultDeclaration": - case "AwaitExpression": - case "JSXSpreadAttribute": - case "TSTypeAssertionExpression": - case "TSAsExpression": - case "TSNonNullExpression": - return true; - - case "NewExpression": - case "CallExpression": - return name === "callee" && parent.callee === node; - - case "ConditionalExpression": - return name === "test" && parent.test === node; - - case "MemberExpression": - return name === "object" && parent.object === node; - - default: - return false; - } - - case "FunctionExpression": - switch (parent.type) { - case "CallExpression": - return name === "callee"; // Not strictly necessary, but it's clearer to the reader if IIFEs are wrapped in parentheses. - case "TaggedTemplateExpression": - return true; // This is basically a kind of IIFE. - case "ExportDefaultDeclaration": - return true; - default: - return false; - } - - case "ArrowFunctionExpression": - switch (parent.type) { - case "CallExpression": - return name === "callee"; - - case "NewExpression": - return name === "callee"; - - case "MemberExpression": - return name === "object"; - - case "TSAsExpression": - case "BindExpression": - case "TaggedTemplateExpression": - case "UnaryExpression": - case "LogicalExpression": - case "BinaryExpression": - case "AwaitExpression": - case "TSTypeAssertionExpression": - return true; - - case "ConditionalExpression": - return name === "test"; - - default: - return false; - } - - case "ClassExpression": - return parent.type === "ExportDefaultDeclaration"; - } - - return false; -}; - -function isStatement(node) { - return ( - node.type === "BlockStatement" || - node.type === "BreakStatement" || - node.type === "ClassBody" || - node.type === "ClassDeclaration" || - node.type === "ClassMethod" || - node.type === "ClassProperty" || - node.type === "ClassPrivateProperty" || - node.type === "ContinueStatement" || - node.type === "DebuggerStatement" || - node.type === "DeclareClass" || - node.type === "DeclareExportAllDeclaration" || - node.type === "DeclareExportDeclaration" || - node.type === "DeclareFunction" || - node.type === "DeclareInterface" || - node.type === "DeclareModule" || - node.type === "DeclareModuleExports" || - node.type === "DeclareVariable" || - node.type === "DoWhileStatement" || - node.type === "ExportAllDeclaration" || - node.type === "ExportDefaultDeclaration" || - node.type === "ExportNamedDeclaration" || - node.type === "ExpressionStatement" || - node.type === "ForAwaitStatement" || - node.type === "ForInStatement" || - node.type === "ForOfStatement" || - node.type === "ForStatement" || - node.type === "FunctionDeclaration" || - node.type === "IfStatement" || - node.type === "ImportDeclaration" || - node.type === "InterfaceDeclaration" || - node.type === "LabeledStatement" || - node.type === "MethodDefinition" || - node.type === "ReturnStatement" || - node.type === "SwitchStatement" || - node.type === "ThrowStatement" || - node.type === "TryStatement" || - node.type === "TSAbstractClassDeclaration" || - node.type === "TSEnumDeclaration" || - node.type === "TSImportEqualsDeclaration" || - node.type === "TSInterfaceDeclaration" || - node.type === "TSModuleDeclaration" || - node.type === "TSNamespaceExportDeclaration" || - node.type === "TypeAlias" || - node.type === "VariableDeclaration" || - node.type === "WhileStatement" || - node.type === "WithStatement" - ); -} - module.exports = FastPath; diff --git a/src/common/load-plugins.js b/src/common/load-plugins.js index 59e73637..b4899784 100644 --- a/src/common/load-plugins.js +++ b/src/common/load-plugins.js @@ -1,9 +1,10 @@ "use strict"; const resolve = require("resolve"); +const readPkgUp = require("read-pkg-up"); -function loadPlugins(options) { - options = Object.assign({ plugins: [] }, options); +function loadPlugins(plugins) { + plugins = plugins || []; const internalPlugins = [ require("../language-js"), @@ -13,20 +14,47 @@ function loadPlugins(options) { require("../language-markdown"), require("../language-html"), require("../language-vue") - ].filter(plugin => { - return options.plugins.indexOf(plugin) < 0; - }); + ]; - const externalPlugins = options.plugins.map(plugin => { - if (typeof plugin !== "string") { - return plugin; + const externalPlugins = plugins + .concat( + getPluginsFromPackage( + readPkgUp.sync({ + normalize: false + }).pkg + ) + ) + .map(plugin => { + if (typeof plugin !== "string") { + return plugin; + } + + const pluginPath = resolve.sync(plugin, { basedir: process.cwd() }); + return Object.assign({ name: plugin }, eval("require")(pluginPath)); + }); + + return deduplicate(internalPlugins.concat(externalPlugins)); +} + +function getPluginsFromPackage(pkg) { + if (!pkg) { + return []; + } + const deps = Object.assign({}, pkg.dependencies, pkg.devDependencies); + return Object.keys(deps).filter( + dep => + dep.startsWith("prettier-plugin-") || dep.startsWith("@prettier/plugin-") + ); +} + +function deduplicate(items) { + const uniqItems = []; + for (const item of items) { + if (uniqItems.indexOf(item) < 0) { + uniqItems.push(item); } - - const pluginPath = resolve.sync(plugin, { basedir: process.cwd() }); - return eval("require")(pluginPath); - }); - - return internalPlugins.concat(externalPlugins); + } + return uniqItems; } module.exports = loadPlugins; diff --git a/src/common/support.js b/src/common/support.js index c153a410..1ebee558 100644 --- a/src/common/support.js +++ b/src/common/support.js @@ -5,6 +5,7 @@ const dedent = require("dedent"); const semver = require("semver"); const currentVersion = require("../../package.json").version; const loadPlugins = require("./load-plugins"); +const cliConstant = require("../cli/constant"); const CATEGORY_GLOBAL = "Global"; const CATEGORY_SPECIAL = "Special"; @@ -14,6 +15,7 @@ const CATEGORY_SPECIAL = "Special"; * @property {string} since - available since version * @property {string} category * @property {'int' | 'boolean' | 'choice' | 'path'} type + * @property {boolean} array - indicate it's an array of the specified type * @property {boolean?} deprecated - deprecated since version * @property {OptionRedirectInfo?} redirect - redirect deprecated option * @property {string} description @@ -21,9 +23,10 @@ const CATEGORY_SPECIAL = "Special"; * @property {OptionValueInfo} default * @property {OptionRangeInfo?} range - for type int * @property {OptionChoiceInfo?} choices - for type choice + * @property {(value: any) => boolean} exception * * @typedef {number | boolean | string} OptionValue - * @typedef {OptionValue | Array<{ since: string, value: OptionValue}>} OptionValueInfo + * @typedef {OptionValue | [{ value: OptionValue[] }] | Array<{ since: string, value: OptionValue}>} OptionValueInfo * * @typedef {Object} OptionRedirectInfo * @property {string} option @@ -40,6 +43,10 @@ const CATEGORY_SPECIAL = "Special"; * @property {string?} since - undefined if available since the first version of the option * @property {string?} deprecated - deprecated since version * @property {OptionValueInfo?} redirect - redirect deprecated value + * + * @property {string?} cliName + * @property {string?} cliCategory + * @property {string?} cliDescription */ /** @type {{ [name: string]: OptionInfo } */ const supportOptions = { @@ -52,7 +59,8 @@ const supportOptions = { description: dedent` Print (to stderr) where a cursor at the given position would move to after formatting. This option cannot be used with --range-start and --range-end. - ` + `, + cliCategory: cliConstant.CATEGORY_EDITOR }, filepath: { since: "1.4.0", @@ -60,14 +68,18 @@ const supportOptions = { type: "path", default: undefined, description: - "Specify the input filepath. This will be used to do parser inference." + "Specify the input filepath. This will be used to do parser inference.", + cliName: "stdin-filepath", + cliCategory: cliConstant.CATEGORY_OTHER, + cliDescription: "Path to the file to pretend that stdin comes from." }, insertPragma: { since: "1.8.0", category: CATEGORY_SPECIAL, type: "boolean", default: false, - description: "Insert @format pragma into file's first docblock comment." + description: "Insert @format pragma into file's first docblock comment.", + cliCategory: cliConstant.CATEGORY_OTHER }, parser: { since: "0.0.10", @@ -75,9 +87,11 @@ const supportOptions = { type: "choice", default: "babylon", description: "Which parser to use.", + exception: value => + typeof value === "string" || typeof value === "function", choices: [ - { value: "babylon", description: "JavaScript" }, { value: "flow", description: "Flow" }, + { value: "babylon", description: "JavaScript" }, { value: "typescript", since: "1.4.0", description: "TypeScript" }, { value: "css", since: "1.7.1", description: "CSS" }, { @@ -91,9 +105,22 @@ const supportOptions = { { value: "scss", since: "1.7.1", description: "SCSS" }, { value: "json", since: "1.5.0", description: "JSON" }, { value: "graphql", since: "1.5.0", description: "GraphQL" }, - { value: "markdown", since: "1.8.0", description: "Markdown" } + { value: "markdown", since: "1.8.0", description: "Markdown" }, + { value: "vue", since: "1.10.0", description: "Vue" } ] }, + plugins: { + since: "1.10.0", + type: "path", + array: true, + default: [{ value: [] }], + category: CATEGORY_GLOBAL, + description: + "Add a plugin. Multiple plugins can be passed as separate `--plugin`s.", + exception: value => typeof value === "string" || typeof value === "object", + cliName: "plugin", + cliCategory: cliConstant.CATEGORY_CONFIG + }, printWidth: { since: "0.0.0", category: CATEGORY_GLOBAL, @@ -112,7 +139,8 @@ const supportOptions = { Format code ending at a given character offset (exclusive). The range will extend forwards to the end of the selected statement. This option cannot be used with --cursor-offset. - ` + `, + cliCategory: cliConstant.CATEGORY_EDITOR }, rangeStart: { since: "1.4.0", @@ -124,7 +152,8 @@ const supportOptions = { Format code starting at a given character offset. The range will extend backwards to the start of the first line containing the selected statement. This option cannot be used with --cursor-offset. - ` + `, + cliCategory: cliConstant.CATEGORY_EDITOR }, requirePragma: { since: "1.7.0", @@ -134,7 +163,8 @@ const supportOptions = { description: dedent` Require either '@prettier' or '@format' to be present in the file's first docblock comment in order for it to be formatted. - ` + `, + cliCategory: cliConstant.CATEGORY_OTHER }, tabWidth: { type: "int", @@ -150,7 +180,8 @@ const supportOptions = { default: false, deprecated: "0.0.10", description: "Use flow parser.", - redirect: { option: "parser", value: "flow" } + redirect: { option: "parser", value: "flow" }, + cliName: "flow-parser" }, useTabs: { since: "1.0.0", @@ -162,32 +193,31 @@ const supportOptions = { }; function getSupportInfo(version, opts) { - opts = opts || {}; + opts = Object.assign( + { + plugins: [], + pluginsLoaded: false, + showUnreleased: false, + showDeprecated: false, + showInternal: false + }, + opts + ); if (!version) { version = currentVersion; } - const plugins = loadPlugins(); + const plugins = opts.pluginsLoaded ? opts.plugins : loadPlugins(opts.plugins); const options = util .arrayify( Object.assign( - plugins - .reduce( - (currentPrinters, plugin) => - currentPrinters.concat( - Object.keys(plugin.printers).map( - printerName => plugin.printers[printerName] - ) - ), - [] - ) - .reduce( - (currentOptions, printer) => - Object.assign(currentOptions, printer.options), - {} - ), + plugins.reduce( + (currentOptions, plugin) => + Object.assign(currentOptions, plugin.options), + {} + ), supportOptions ), "name" @@ -196,15 +226,19 @@ function getSupportInfo(version, opts) { .filter(filterSince) .filter(filterDeprecated) .map(mapDeprecated) + .map(mapInternal) .map(option => { const newOption = Object.assign({}, option); if (Array.isArray(newOption.default)) { - newOption.default = newOption.default - .filter(filterSince) - .sort((info1, info2) => - semver.compare(info2.since, info1.since) - )[0].value; + newOption.default = + newOption.default.length === 1 + ? newOption.default[0].value + : newOption.default + .filter(filterSince) + .sort((info1, info2) => + semver.compare(info2.since, info1.since) + )[0].value; } if (Array.isArray(newOption.choices)) { @@ -215,13 +249,28 @@ function getSupportInfo(version, opts) { } return newOption; + }) + .map(option => { + const filteredPlugins = plugins.filter( + plugin => plugin.defaultOptions && plugin.defaultOptions[option.name] + ); + const pluginDefaults = filteredPlugins.reduce((reduced, plugin) => { + reduced[plugin.name] = plugin.defaultOptions[option.name]; + return reduced; + }, {}); + return Object.assign(option, { pluginDefaults }); }); const usePostCssParser = semver.lt(version, "1.7.1"); const languages = plugins .reduce((all, plugin) => all.concat(plugin.languages), []) - .filter(language => language.since && semver.gte(version, language.since)) + .filter( + language => + language.since + ? semver.gte(version, language.since) + : language.since !== null + ) .map(language => { // Prevent breaking changes if (language.name === "Markdown") { @@ -268,6 +317,16 @@ function getSupportInfo(version, opts) { delete newObject.redirect; return newObject; } + function mapInternal(object) { + if (opts.showInternal) { + return object; + } + const newObject = Object.assign({}, object); + delete newObject.cliName; + delete newObject.cliCategory; + delete newObject.cliDescription; + return newObject; + } } module.exports = { diff --git a/src/common/util-shared.js b/src/common/util-shared.js new file mode 100644 index 00000000..f4463fa0 --- /dev/null +++ b/src/common/util-shared.js @@ -0,0 +1,26 @@ +"use strict"; + +const util = require("./util"); + +function isNextLineEmpty(text, node, options) { + return util.isNextLineEmpty(text, node, options.locEnd); +} + +function getNextNonSpaceNonCommentCharacterIndex(text, node, options) { + return util.getNextNonSpaceNonCommentCharacterIndex( + text, + node, + options.locEnd + ); +} + +module.exports = { + isNextLineEmpty, + isNextLineEmptyAfterIndex: util.isNextLineEmptyAfterIndex, + getNextNonSpaceNonCommentCharacterIndex, + mapDoc: util.mapDoc, + makeString: util.makeString, + addLeadingComment: util.addLeadingComment, + addDanglingComment: util.addDanglingComment, + addTrailingComment: util.addTrailingComment +}; diff --git a/src/common/util.js b/src/common/util.js index dd856448..f9a9975d 100644 --- a/src/common/util.js +++ b/src/common/util.js @@ -187,7 +187,7 @@ function hasNewlineInRange(text, start, end) { } // Note: this function doesn't ignore leading comments unlike isNextLineEmpty -function isPreviousLineEmpty(text, node) { +function isPreviousLineEmpty(text, node, locStart) { let idx = locStart(node) - 1; idx = skipSpaces(text, idx, { backwards: true }); idx = skipNewline(text, idx, { backwards: true }); @@ -211,11 +211,11 @@ function isNextLineEmptyAfterIndex(text, index) { return hasNewline(text, idx); } -function isNextLineEmpty(text, node) { +function isNextLineEmpty(text, node, locEnd) { return isNextLineEmptyAfterIndex(text, locEnd(node)); } -function getNextNonSpaceNonCommentCharacterIndex(text, node) { +function getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd) { let oldIdx = null; let idx = locEnd(node); while (idx !== oldIdx) { @@ -228,8 +228,10 @@ function getNextNonSpaceNonCommentCharacterIndex(text, node) { return idx; } -function getNextNonSpaceNonCommentCharacter(text, node) { - return text.charAt(getNextNonSpaceNonCommentCharacterIndex(text, node)); +function getNextNonSpaceNonCommentCharacter(text, node, locEnd) { + return text.charAt( + getNextNonSpaceNonCommentCharacterIndex(text, node, locEnd) + ); } function hasSpaces(text, index, opts) { @@ -238,65 +240,6 @@ function hasSpaces(text, index, opts) { return idx !== index; } -function locStart(node) { - // Handle nodes with decorators. They should start at the first decorator - if ( - node.declaration && - node.declaration.decorators && - node.declaration.decorators.length > 0 - ) { - return locStart(node.declaration.decorators[0]); - } - if (node.decorators && node.decorators.length > 0) { - return locStart(node.decorators[0]); - } - - if (node.__location) { - return node.__location.startOffset; - } - if (node.range) { - return node.range[0]; - } - if (typeof node.start === "number") { - return node.start; - } - if (node.source) { - return lineColumnToIndex(node.source.start, node.source.input.css) - 1; - } - if (node.loc) { - return node.loc.start; - } -} - -function locEnd(node) { - const endNode = node.nodes && getLast(node.nodes); - if (endNode && node.source && !node.source.end) { - node = endNode; - } - - let loc; - if (node.range) { - loc = node.range[1]; - } else if (typeof node.end === "number") { - loc = node.end; - } else if (node.source) { - loc = lineColumnToIndex(node.source.end, node.source.input.css); - } - - if (node.__location) { - return node.__location.endOffset; - } - if (node.typeAnnotation) { - return Math.max(loc, locEnd(node.typeAnnotation)); - } - - if (node.loc && !loc) { - return node.loc.end; - } - - return loc; -} - // Super inefficient, needs to be cached. function lineColumnToIndex(lineColumn, text) { let index = 0; @@ -476,7 +419,7 @@ function isBlockComment(comment) { return comment.type === "Block" || comment.type === "CommentBlock"; } -function hasClosureCompilerTypeCastComment(text, node) { +function hasClosureCompilerTypeCastComment(text, node, locEnd) { // https://github.com/google/closure-compiler/wiki/Annotating-Types#type-casts // Syntax example: var x = /** @type {string} */ (fruit); return ( @@ -486,7 +429,7 @@ function hasClosureCompilerTypeCastComment(text, node) { comment.leading && isBlockComment(comment) && comment.value.match(/^\*\s*@type\s*{[^}]+}\s*$/) && - getNextNonSpaceNonCommentCharacter(text, comment) === "(" + getNextNonSpaceNonCommentCharacter(text, comment, locEnd) === "(" ) ); } @@ -813,6 +756,37 @@ function arrayify(object, keyName) { ); } +function addCommentHelper(node, comment) { + const comments = node.comments || (node.comments = []); + comments.push(comment); + comment.printed = false; + + // For some reason, TypeScript parses `// x` inside of JSXText as a comment + // We already "print" it via the raw text, we don't need to re-print it as a + // comment + if (node.type === "JSXText") { + comment.printed = true; + } +} + +function addLeadingComment(node, comment) { + comment.leading = true; + comment.trailing = false; + addCommentHelper(node, comment); +} + +function addDanglingComment(node, comment) { + comment.leading = false; + comment.trailing = false; + addCommentHelper(node, comment); +} + +function addTrailingComment(node, comment) { + comment.leading = false; + comment.trailing = true; + addCommentHelper(node, comment); +} + module.exports = { arrayify, punctuationRegex, @@ -839,8 +813,6 @@ module.exports = { hasNewline, hasNewlineInRange, hasSpaces, - locStart, - locEnd, setLocStart, setLocEnd, startsWithNoLookaheadToken, @@ -852,5 +824,10 @@ module.exports = { printString, printNumber, hasIgnoreComment, - hasNodeIgnoreComment + hasNodeIgnoreComment, + lineColumnToIndex, + makeString, + addLeadingComment, + addDanglingComment, + addTrailingComment }; diff --git a/src/config/resolve-config-editorconfig.js b/src/config/resolve-config-editorconfig.js index e113469b..07c20925 100644 --- a/src/config/resolve-config-editorconfig.js +++ b/src/config/resolve-config-editorconfig.js @@ -9,7 +9,7 @@ const findProjectRoot = require("find-project-root"); const maybeParse = (filePath, config, parse) => { const root = findProjectRoot(path.dirname(path.resolve(filePath))); - return filePath && !config && parse(filePath, { root }); + return filePath && parse(filePath, { root }); }; const editorconfigAsyncNoCache = (filePath, config) => { diff --git a/src/doc/doc-builders.js b/src/doc/doc-builders.js index d99c6f47..23a90a0c 100644 --- a/src/doc/doc-builders.js +++ b/src/doc/doc-builders.js @@ -56,6 +56,18 @@ function group(contents, opts) { }; } +function dedentToRoot(contents) { + return align(-Infinity, contents); +} + +function markAsRoot(contents) { + return align({ type: "root" }, contents); +} + +function dedent(contents) { + return align(-1, contents); +} + function conditionalGroup(states, opts) { return group( states[0], @@ -149,5 +161,8 @@ module.exports = { ifBreak, indent, align, - addAlignmentToDoc + addAlignmentToDoc, + markAsRoot, + dedentToRoot, + dedent }; diff --git a/src/doc/doc-debug.js b/src/doc/doc-debug.js index 80e117a8..415b7199 100644 --- a/src/doc/doc-debug.js +++ b/src/doc/doc-debug.js @@ -68,7 +68,17 @@ function printDoc(doc) { } if (doc.type === "align") { - return "align(" + doc.n + ", " + printDoc(doc.contents) + ")"; + return doc.n === -Infinity + ? "dedentToRoot(" + printDoc(doc.contents) + ")" + : doc.n < 0 + ? "dedent(" + printDoc(doc.contents) + ")" + : doc.n.type === "root" + ? "markAsRoot(" + printDoc(doc.contents) + ")" + : "align(" + + JSON.stringify(doc.n) + + ", " + + printDoc(doc.contents) + + ")"; } if (doc.type === "if-break") { diff --git a/src/doc/doc-printer.js b/src/doc/doc-printer.js index 813f3269..422044f2 100644 --- a/src/doc/doc-printer.js +++ b/src/doc/doc-printer.js @@ -10,33 +10,103 @@ const MODE_BREAK = 1; const MODE_FLAT = 2; function rootIndent() { - return { - length: 0, - value: "" - }; + return { value: "", length: 0, queue: [] }; } function makeIndent(ind, options) { - return { - length: ind.length + options.tabWidth, - value: ind.value + (options.useTabs ? "\t" : " ".repeat(options.tabWidth)) - }; + return generateInd(ind, { type: "indent" }, options); } function makeAlign(ind, n, options) { return n === -Infinity - ? rootIndent() - : typeof n === "string" - ? { - length: ind.length + n.length, - value: ind.value + n + ? ind.root || rootIndent() + : n < 0 + ? generateInd(ind, { type: "dedent" }, options) + : !n + ? ind + : n.type === "root" + ? Object.assign({}, ind, { root: ind }) + : typeof n === "string" + ? generateInd(ind, { type: "stringAlign", n }, options) + : generateInd(ind, { type: "numberAlign", n }, options); +} + +function generateInd(ind, newPart, options) { + const queue = + newPart.type === "dedent" + ? ind.queue.slice(0, -1) + : ind.queue.concat(newPart); + + let value = ""; + let length = 0; + let lastTabs = 0; + let lastSpaces = 0; + + for (const part of queue) { + switch (part.type) { + case "indent": + flush(); + if (options.useTabs) { + addTabs(1); + } else { + addSpaces(options.tabWidth); } - : options.useTabs && n > 0 - ? makeIndent(ind, options) - : { - length: ind.length + n, - value: ind.value + " ".repeat(n) - }; + break; + case "stringAlign": + flush(); + value += part.n; + length += part.n.length; + break; + case "numberAlign": + lastTabs += 1; + lastSpaces += part.n; + break; + /* istanbul ignore next */ + default: + throw new Error(`Unexpected type '${part.type}'`); + } + } + + flushSpaces(); + + return Object.assign({}, ind, { value, length, queue }); + + function addTabs(count) { + value += "\t".repeat(count); + length += options.tabWidth * count; + } + + function addSpaces(count) { + value += " ".repeat(count); + length += count; + } + + function flush() { + if (options.useTabs) { + flushTabs(); + } else { + flushSpaces(); + } + } + + function flushTabs() { + if (lastTabs > 0) { + addTabs(lastTabs); + } + resetLast(); + } + + function flushSpaces() { + if (lastSpaces > 0) { + addSpaces(lastSpaces); + } + resetLast(); + } + + function resetLast() { + lastTabs = 0; + lastSpaces = 0; + } } function fits(next, restCommands, width, options, mustBeFlat) { @@ -380,8 +450,13 @@ function printDocToString(doc, options) { } if (doc.literal) { - out.push(newLine); - pos = 0; + if (ind.root) { + out.push(newLine, ind.root.value); + pos = ind.root.length; + } else { + out.push(newLine); + pos = 0; + } } else { if (out.length > 0) { // Trim whitespace at the end of line diff --git a/src/language-css/clean.js b/src/language-css/clean.js index 1224e9f9..7bfe7872 100644 --- a/src/language-css/clean.js +++ b/src/language-css/clean.js @@ -1,5 +1,7 @@ "use strict"; +const htmlTagNames = require("html-tag-names"); + function clean(ast, newObj) { if ( ast.type === "media-query" || @@ -22,7 +24,11 @@ function clean(ast, newObj) { } if ( - (ast.type === "value-word" && ast.isColor && ast.isHex) || + (ast.type === "value-word" && + ((ast.isColor && ast.isHex) || + ["initial", "inherit", "unset", "revert"].indexOf( + newObj.value.replace().toLowerCase() + ) !== -1)) || ast.type === "media-feature" || ast.type === "selector-root-invalid" || ast.type === "selector-pseudo" @@ -61,9 +67,23 @@ function clean(ast, newObj) { newObj.importPath = cleanCSSStrings(newObj.importPath); } - if (ast.type === "selector-attribute" && newObj.value) { - newObj.value = newObj.value.replace(/^['"]|['"]$/g, ""); - delete newObj.quoted; + if (ast.type === "selector-attribute") { + newObj.attribute = newObj.attribute.trim(); + + if (newObj.namespace) { + if (typeof newObj.namespace === "string") { + newObj.namespace = newObj.namespace.trim(); + + if (newObj.namespace.length === 0) { + newObj.namespace = true; + } + } + } + + if (newObj.value) { + newObj.value = newObj.value.trim().replace(/^['"]|['"]$/g, ""); + delete newObj.quoted; + } } if ( @@ -84,6 +104,24 @@ function clean(ast, newObj) { } ); } + + if (ast.type === "media-url") { + newObj.value = newObj.value + .replace(/^url\(\s+/gi, "url(") + .replace(/\s+\)$/gi, ")"); + } + + if (ast.type === "selector-tag") { + const lowercasedValue = ast.value.toLowerCase(); + + if (htmlTagNames.indexOf(lowercasedValue) !== -1) { + newObj.value = lowercasedValue; + } + + if (["from", "to"].indexOf(lowercasedValue) !== -1) { + newObj.value = lowercasedValue; + } + } } function cleanCSSStrings(value) { diff --git a/src/language-css/index.js b/src/language-css/index.js index b52dd49f..07008c87 100644 --- a/src/language-css/index.js +++ b/src/language-css/index.js @@ -1,6 +1,11 @@ "use strict"; const printer = require("./printer-postcss"); +const options = require("./options"); +const privateUtil = require("../common/util"); + +const lineColumnToIndex = privateUtil.lineColumnToIndex; +const getLast = privateUtil.getLast; // Based on: // https://github.com/github/linguist/blob/master/lib/linguist/languages.yml @@ -51,7 +56,23 @@ const postcss = { get parse() { return eval("require")("./parser-postcss"); }, - astFormat: "postcss" + astFormat: "postcss", + locEnd: function(node) { + const endNode = node.nodes && getLast(node.nodes); + if (endNode && node.source && !node.source.end) { + node = endNode; + } + if (node.source) { + return lineColumnToIndex(node.source.end, node.source.input.css); + } + return null; + }, + locStart: function(node) { + if (node.source) { + return lineColumnToIndex(node.source.start, node.source.input.css) - 1; + } + return null; + } }; // TODO: switch these to just `postcss` and use `language` instead. @@ -67,6 +88,7 @@ const printers = { module.exports = { languages, + options, parsers, printers }; diff --git a/src/language-css/parser-postcss.js b/src/language-css/parser-postcss.js index 56723381..23c119a0 100644 --- a/src/language-css/parser-postcss.js +++ b/src/language-css/parser-postcss.js @@ -1,8 +1,19 @@ "use strict"; const createError = require("../common/parser-create-error"); +const grayMatter = require("gray-matter"); function parseSelector(selector) { + // If there's a comment inside of a selector, the parser tries to parse + // the content of the comment as selectors which turns it into complete + // garbage. Better to print the whole selector as-is and not try to parse + // and reformat it. + if (selector.match(/\/\/|\/\*/)) { + return { + type: "selector-comment", + value: selector.replace(/^ +/, "").replace(/ +$/, "") + }; + } const selectorParser = require("postcss-selector-parser"); let result; selectorParser(result_ => { @@ -28,6 +39,23 @@ function parseValueNodes(nodes) { for (let i = 0; i < nodes.length; ++i) { const node = nodes[i]; + const isUnquotedDataURLCall = + node.type === "func" && + node.value === "url" && + node.group && + node.group.groups && + node.group.groups[0] && + node.group.groups[0].groups && + node.group.groups[0].groups.length > 2 && + node.group.groups[0].groups[0].type === "word" && + node.group.groups[0].groups[0].value === "data" && + node.group.groups[0].groups[1].type === "colon" && + node.group.groups[0].groups[1].value === ":"; + + if (isUnquotedDataURLCall) { + node.group.groups = [stringifyGroup(node)]; + } + if (node.type === "paren" && node.value === "(") { parenGroup = { open: node, @@ -75,6 +103,31 @@ function parseValueNodes(nodes) { return rootParenGroup; } +function stringifyGroup(node) { + if (node.group) { + return stringifyGroup(node.group); + } + + if (node.groups) { + return node.groups.reduce((previousValue, currentValue, index) => { + return ( + previousValue + + stringifyGroup(currentValue) + + (currentValue.type === "comma_group" && index !== node.groups.length - 1 + ? "," + : "") + ); + }, ""); + } + + const before = node.raws && node.raws.before ? node.raws.before : ""; + const value = node.value ? node.value : ""; + const unit = node.unit ? node.unit : ""; + const after = node.raws && node.raws.after ? node.raws.after : ""; + + return before + value + unit + after; +} + function flattenGroups(node) { if ( node.type === "paren_group" && @@ -151,19 +204,33 @@ function parseMediaQuery(value) { return addTypePrefix(result, "media-"); } +const DEFAULT_SCSS_DIRECTIVE = /(\s*?)(!default).*$/; +const GLOBAL_SCSS_DIRECTIVE = /(\s*?)(!global).*$/; + function parseNestedCSS(node) { if (node && typeof node === "object") { delete node.parent; + for (const key in node) { parseNestedCSS(node[key]); } - if (typeof node.selector === "string") { - const selector = node.raws.selector + + if (typeof node.selector === "string" && node.selector.trim().length > 0) { + let selector = node.raws.selector ? node.raws.selector.raw : node.selector; + if (node.raws.between && node.raws.between.trim()) { + selector += node.raws.between; + } + + if (selector.startsWith("@") && selector.endsWith(":")) { + return node; + } + try { node.selector = parseSelector(selector); + node.raws.selector = selector; } catch (e) { // Fail silently. It's better to print it as is than to try and parse it // Note: A common failure is for SCSS nested properties. `background: @@ -176,25 +243,171 @@ function parseNestedCSS(node) { value: selector }; } + + return node; } - if (node.type && typeof node.value === "string") { + + if ( + node.type && + node.type !== "css-comment-yaml" && + typeof node.value === "string" && + node.value.trim().length > 0 + ) { try { - node.value = parseValue(node.value); + let value = node.raws.value ? node.raws.value.raw : node.value; + + const defaultSCSSDirectiveIndex = value.match(DEFAULT_SCSS_DIRECTIVE); + + if (defaultSCSSDirectiveIndex) { + value = value.substring(0, defaultSCSSDirectiveIndex.index); + node.scssDefault = true; + + if (defaultSCSSDirectiveIndex[0].trim() !== "!default") { + node.raws.scssDefault = defaultSCSSDirectiveIndex[0]; + } + } + + const globalSCSSDirectiveIndex = value.match(GLOBAL_SCSS_DIRECTIVE); + + if (globalSCSSDirectiveIndex) { + value = value.substring(0, globalSCSSDirectiveIndex.index); + node.scssGlobal = true; + + if (globalSCSSDirectiveIndex[0].trim() !== "!global") { + node.raws.scssGlobal = globalSCSSDirectiveIndex[0]; + } + } + + if (value.startsWith("progid:")) { + return node; + } + + node.value = parseValue(value); } catch (e) { throw createError( "(postcss-values-parser) " + e.toString(), node.source ); } + + return node; } + if (node.type === "css-atrule" && typeof node.params === "string") { - node.params = parseMediaQuery(node.params); + let params = + node.raws.params && node.raws.params.raw + ? node.raws.params.raw + : node.params; + + if (node.raws.afterName.trim()) { + params = node.raws.afterName + params; + } + + if (node.raws.between.trim()) { + params = params + node.raws.between; + } + + params = params.trim(); + + if (params.length === 0) { + return node; + } + + const name = node.name; + const lowercasedName = node.name.toLowerCase(); + + if (name === "warn" || name === "error") { + node.params = { + type: "media-unknown", + value: params + }; + + return node; + } + + if (name === "extend" || name === "nest") { + node.selector = parseSelector(params); + delete node.params; + + return node; + } + + if (name === "at-root") { + if (/^\(\s*(without|with)\s*:[\s\S]+\)$/.test(params)) { + node.params = parseMediaQuery(params); + } else { + node.selector = parseSelector(params); + delete node.params; + } + + return node; + } + + if ( + [ + "if", + "else", + "for", + "each", + "while", + "debug", + "mixin", + "include", + "function", + "return", + "define-mixin", + "add-mixin" + ].indexOf(name) !== -1 + ) { + // Remove unnecessary spaces in SCSS variable arguments + params = params.replace(/(\$\S+?)\s+?\.\.\./, "$1..."); + // Remove unnecessary spaces before SCSS control, mixin and function directives + params = params.replace(/^(?!if)(\S+)\s+\(/, "$1("); + + node.value = parseValue(params); + delete node.params; + + return node; + } + + if (name === "custom-selector") { + const customSelector = params.match(/:--\S+?\s+/)[0].trim(); + + node.customSelector = customSelector; + node.selector = parseSelector(params.substring(customSelector.length)); + delete node.params; + + return node; + } + + if ( + ["namespace", "import", "media", "supports", "custom-media"].indexOf( + lowercasedName + ) !== -1 + ) { + if (params.includes("#{")) { + // Workaround for media at rule with scss interpolation + return { + type: "media-unknown", + value: params + }; + } + + node.params = parseMediaQuery(params); + + return node; + } + + node.params = params; + + return node; } } + return node; } -function parseWithParser(parser, text) { +function parseWithParser(parser, text, frontMatter) { let result; try { result = parser.parse(text); @@ -204,6 +417,14 @@ function parseWithParser(parser, text) { } throw createError("(postcss) " + e.name + " " + e.reason, { start: e }); } + + if (Object.keys(frontMatter.data).length > 0) { + result.nodes.unshift({ + type: "comment-yaml", + value: grayMatter.stringify("", frontMatter.data).replace(/\s$/, "") + }); + } + const prefixedResult = addTypePrefix(result, "css-"); const parsedResult = parseNestedCSS(prefixedResult); return parsedResult; @@ -237,15 +458,22 @@ function parse(text, parsers, opts) { ? opts.parser === "scss" : IS_POSSIBLY_SCSS.test(text); + const frontMatter = grayMatter(text); + const normalizedText = frontMatter.content; + try { - return parseWithParser(requireParser(isSCSS), text); + return parseWithParser(requireParser(isSCSS), normalizedText, frontMatter); } catch (originalError) { if (hasExplicitParserChoice) { throw originalError; } try { - return parseWithParser(requireParser(!isSCSS), text); + return parseWithParser( + requireParser(!isSCSS), + normalizedText, + frontMatter + ); } catch (_secondError) { throw originalError; } diff --git a/src/language-css/printer-postcss.js b/src/language-css/printer-postcss.js index 794ae803..d2e2ef81 100644 --- a/src/language-css/printer-postcss.js +++ b/src/language-css/printer-postcss.js @@ -1,7 +1,9 @@ "use strict"; +const htmlTagNames = require("html-tag-names"); const clean = require("./clean"); -const util = require("../common/util"); +const privateUtil = require("../common/util"); +const sharedUtil = require("../common/util-shared"); const doc = require("../doc"); const docBuilders = doc.builders; const concat = docBuilders.concat; @@ -12,36 +14,46 @@ const softline = docBuilders.softline; const group = docBuilders.group; const fill = docBuilders.fill; const indent = docBuilders.indent; -const printerOptions = require("./options"); const removeLines = doc.utils.removeLines; function genericPrint(path, options, print) { - const n = path.getValue(); + const node = path.getValue(); /* istanbul ignore if */ - if (!n) { + if (!node) { return ""; } - if (typeof n === "string") { - return n; + if (typeof node === "string") { + return node; } - switch (n.type) { + switch (node.type) { + case "css-comment-yaml": + return node.value; case "css-root": { - return concat([printNodeSequence(path, options, print), hardline]); + const nodes = printNodeSequence(path, options, print); + + if (nodes.parts.length) { + return concat([nodes, hardline]); + } + + return nodes; } case "css-comment": { - if (n.raws.content) { - return n.raws.content; + if (node.raws.content) { + return node.raws.content; } - const text = options.originalText.slice(util.locStart(n), util.locEnd(n)); - const rawText = n.raws.text || n.text; + const text = options.originalText.slice( + options.locStart(node), + options.locEnd(node) + ); + const rawText = node.raws.text || node.text; // Workaround a bug where the location is off. // https://github.com/postcss/postcss-scss/issues/63 if (text.indexOf(rawText) === -1) { - if (n.raws.inline) { + if (node.raws.inline) { return concat(["// ", rawText]); } return concat(["/* ", rawText, " */"]); @@ -51,17 +63,18 @@ function genericPrint(path, options, print) { case "css-rule": { return concat([ path.call(print, "selector"), - n.important ? " !important" : "", - n.nodes + node.important ? " !important" : "", + node.nodes ? concat([ " {", - n.nodes.length > 0 + node.nodes.length > 0 ? indent( concat([hardline, printNodeSequence(path, options, print)]) ) : "", hardline, - "}" + "}", + isDetachedRulesetDeclaration(node) ? ";" : "" ]) : ";" ]); @@ -72,25 +85,39 @@ function genericPrint(path, options, print) { // less files with less, but we can hardcode this to work with scss as // well. const isValueExtend = - n.value.type === "value-root" && - n.value.group.type === "value-value" && - n.value.group.group.type === "value-func" && - n.value.group.group.value === "extend"; + node.value.type === "value-root" && + node.value.group.type === "value-value" && + node.value.group.group.type === "value-func" && + node.value.group.group.value === "extend"; const isComposed = - n.value.type === "value-root" && - n.value.group.type === "value-value" && - n.prop === "composes"; + node.value.type === "value-root" && + node.value.group.type === "value-value" && + node.prop === "composes"; + const ruleAncestorNode = getAncestorNode(path, "css-rule"); + const isiCSS = + ruleAncestorNode && + ruleAncestorNode.raws.selector && + (ruleAncestorNode.raws.selector.startsWith(":import") || + ruleAncestorNode.raws.selector.startsWith(":export")); return concat([ - n.raws.before.replace(/[\s;]/g, ""), - maybeToLowerCase(n.prop), - ":", + node.raws.before.replace(/[\s;]/g, ""), + isiCSS ? node.prop : maybeToLowerCase(node.prop), + node.raws.between.trim() === ":" ? ":" : node.raws.between.trim(), isValueExtend ? "" : " ", isComposed ? removeLines(path.call(print, "value")) : path.call(print, "value"), - n.important ? " !important" : "", - n.nodes + node.raws.important + ? node.raws.important.replace(/\s*!\s*important/i, " !important") + : node.important ? " !important" : "", + node.raws.scssDefault + ? node.raws.scssDefault.replace(/\s*!default/i, " !default") + : node.scssDefault ? " !default" : "", + node.raws.scssGlobal + ? node.raws.scssGlobal.replace(/\s*!global/i, " !global") + : node.scssGlobal ? " !global" : "", + node.nodes ? concat([ " {", indent( @@ -104,32 +131,50 @@ function genericPrint(path, options, print) { } case "css-atrule": { const hasParams = - n.params && - !(n.params.type === "media-query-list" && n.params.value === ""); - const isDetachedRulesetCall = - hasParams && - n.params.type === "media-query-list" && - /^\(\s*\)$/.test(n.params.value); + node.params && + !(node.params.type === "media-query-list" && node.params.value === ""); + const isDetachedRulesetCall = hasParams && /^\(\s*\)$/.test(node.params); + const hasParensAround = + node.value && + node.value.group.group.type === "value-paren_group" && + node.value.group.group.open !== null && + node.value.group.group.close !== null; + return concat([ "@", // If a Less file ends up being parsed with the SCSS parser, Less // variable declarations will be parsed as atrules with names ending // with a colon, so keep the original case then. - isDetachedRulesetCall || n.name.endsWith(":") - ? n.name - : maybeToLowerCase(n.name), + isDetachedRulesetCall || node.name.endsWith(":") + ? node.name + : maybeToLowerCase(node.name), hasParams ? concat([ isDetachedRulesetCall ? "" : " ", path.call(print, "params") ]) : "", - n.nodes + node.selector + ? indent(concat([" ", path.call(print, "selector")])) + : "", + node.value + ? group( + concat([ + " ", + path.call(print, "value"), + isControlDirectiveNode(node) + ? hasParensAround ? " " : line + : "" + ]) + ) + : node.name === "else" ? " " : "", + node.nodes ? concat([ - " {", + isControlDirectiveNode(node) ? "" : " ", + "{", indent( concat([ - n.nodes.length > 0 ? softline : "", + node.nodes.length > 0 ? softline : "", printNodeSequence(path, options, print) ]) ), @@ -142,11 +187,11 @@ function genericPrint(path, options, print) { case "css-import": { return concat([ "@", - maybeToLowerCase(n.name), + maybeToLowerCase(node.name), " ", - n.directives ? concat([n.directives, " "]) : "", - adjustStrings(n.importPath, options), - n.nodes.length > 0 + node.directives ? concat([node.directives, " "]) : "", + adjustStrings(node.importPath, options), + node.nodes.length > 0 ? concat([ " {", indent( @@ -168,190 +213,432 @@ function genericPrint(path, options, print) { } parts.push(childPath.call(print)); }, "nodes"); - return group(indent(join(concat([",", line]), parts))); + + return group(indent(join(line, parts))); } case "media-query": { - return join(" ", path.map(print, "nodes")); + return concat([ + join(" ", path.map(print, "nodes")), + isLastNode(path, node) ? "" : "," + ]); } case "media-type": { - const parent = path.getParentNode(2); + const atRuleAncestorNode = getAncestorNode(path, "css-atrule"); if ( - parent.type === "css-atrule" && - parent.name.toLowerCase() === "charset" + atRuleAncestorNode && + atRuleAncestorNode.name.toLowerCase() === "charset" ) { - return n.value; + return node.value; } - return adjustNumbers(adjustStrings(n.value, options)); + return adjustNumbers(adjustStrings(node.value, options)); } case "media-feature-expression": { - if (!n.nodes) { - return n.value; + if (!node.nodes) { + return node.value; } return concat(["(", concat(path.map(print, "nodes")), ")"]); } case "media-feature": { return maybeToLowerCase( - adjustStrings(n.value.replace(/ +/g, " "), options) + adjustStrings(node.value.replace(/ +/g, " "), options) ); } case "media-colon": { - return concat([n.value, " "]); + return concat([node.value, " "]); } case "media-value": { - return adjustNumbers(adjustStrings(n.value, options)); + return adjustNumbers(adjustStrings(node.value, options)); } case "media-keyword": { - return adjustStrings(n.value, options); + return adjustStrings(node.value, options); } case "media-url": { - return adjustStrings(n.value, options); + return adjustStrings( + node.value.replace(/^url\(\s+/gi, "url(").replace(/\s+\)$/gi, ")"), + options + ); } case "media-unknown": { - return adjustStrings(n.value, options); + return adjustStrings(node.value, options); } // postcss-selector-parser case "selector-root-invalid": { // This is likely a SCSS nested property: `background: { color: red; }`. - return adjustNumbers(adjustStrings(maybeToLowerCase(n.value), options)); + return adjustNumbers( + adjustStrings(maybeToLowerCase(node.value), options) + ); } case "selector-root": { - return group(join(concat([",", hardline]), path.map(print, "nodes"))); + const atRuleAncestorNode = getAncestorNode(path, "css-atrule"); + const insideAtRuleNode = + atRuleAncestorNode && + ["extend", "custom-selector", "nest"].indexOf( + atRuleAncestorNode.name + ) !== -1; + + return group( + concat([ + atRuleAncestorNode && atRuleAncestorNode.name === "custom-selector" + ? concat([atRuleAncestorNode.customSelector, line]) + : "", + join( + concat([",", insideAtRuleNode ? line : hardline]), + path.map(print, "nodes") + ) + ]) + ); } case "selector-comment": { - return n.value; + return node.value; } case "selector-string": { - return adjustStrings(n.value, options); + return adjustStrings(node.value, options); } case "selector-tag": { - return adjustNumbers(n.value); + const parentNode = path.getParentNode(); + const index = parentNode && parentNode.nodes.indexOf(node); + const prevNode = index && parentNode.nodes[index - 1]; + + return concat([ + node.namespace + ? concat([node.namespace === true ? "" : node.namespace.trim(), "|"]) + : "", + prevNode.type === "selector-nesting" + ? node.value + : adjustNumbers( + isHTMLTag(node.value) || + isKeyframeAtRuleKeywords(path, node.value) + ? node.value.toLowerCase() + : node.value + ) + ]); } case "selector-id": { - return concat(["#", n.value]); + return concat(["#", node.value]); } case "selector-class": { - return concat([".", adjustNumbers(adjustStrings(n.value, options))]); + return concat([".", adjustNumbers(adjustStrings(node.value, options))]); } case "selector-attribute": { return concat([ "[", - n.attribute, - n.operator ? n.operator : "", - n.value - ? quoteAttributeValue(adjustStrings(n.value, options), options) + node.namespace + ? concat([node.namespace === true ? "" : node.namespace.trim(), "|"]) : "", - n.insensitive ? " i" : "", + node.attribute.trim(), + node.operator ? node.operator : "", + node.value + ? quoteAttributeValue( + adjustStrings(node.value.trim(), options), + options + ) + : "", + node.insensitive ? " i" : "", "]" ]); } case "selector-combinator": { - if (n.value === "+" || n.value === ">" || n.value === "~") { - const parent = path.getParentNode(); + if ( + node.value === "+" || + node.value === ">" || + node.value === "~" || + node.value === ">>>" + ) { + const parentNode = path.getParentNode(); const leading = - parent.type === "selector-selector" && parent.nodes[0] === n + parentNode.type === "selector-selector" && + parentNode.nodes[0] === node ? "" : line; - return concat([leading, n.value, " "]); + return concat([leading, node.value, isLastNode(path, node) ? "" : " "]); } - const leading = n.value.trim().startsWith("(") ? line : ""; + const leading = node.value.trim().startsWith("(") ? line : ""; const value = - adjustNumbers(adjustStrings(n.value.trim(), options)) || line; + adjustNumbers(adjustStrings(node.value.trim(), options)) || line; return concat([leading, value]); } case "selector-universal": { - return n.value; + return concat([ + node.namespace + ? concat([node.namespace === true ? "" : node.namespace.trim(), "|"]) + : "", + adjustNumbers(node.value) + ]); } case "selector-selector": { return group(indent(concat(path.map(print, "nodes")))); } case "selector-pseudo": { return concat([ - maybeToLowerCase(n.value), - n.nodes && n.nodes.length > 0 + maybeToLowerCase(node.value), + node.nodes && node.nodes.length > 0 ? concat(["(", join(", ", path.map(print, "nodes")), ")"]) : "" ]); } case "selector-nesting": { - return printValue(n.value); + return node.value; } // postcss-values-parser case "value-root": { return path.call(print, "group"); } + case "value-comment": { + return concat([ + node.inline ? "//" : "/*", + node.value, + node.inline ? "" : "*/" + ]); + } case "value-comma_group": { - const parent = path.getParentNode(); - let declParent; - let i = 0; - do { - declParent = path.getParentNode(i++); - } while (declParent && declParent.type !== "css-decl"); - - const declParentProp = declParent.prop.toLowerCase(); + const parentNode = path.getParentNode(); + const declAncestorNode = getAncestorNode(path, "css-decl"); + const declAncestorProp = + declAncestorNode && + declAncestorNode.prop && + declAncestorNode.prop.toLowerCase(); const isGridValue = - parent.type === "value-value" && - (declParentProp === "grid" || - declParentProp.startsWith("grid-template")); + declAncestorProp && + parentNode.type === "value-value" && + (declAncestorProp === "grid" || + declAncestorProp.startsWith("grid-template")); + const atRuleAncestorNode = getAncestorNode(path, "css-atrule"); + const isControlDirective = + atRuleAncestorNode && isControlDirectiveNode(atRuleAncestorNode); const printed = path.map(print, "groups"); const parts = []; + const functionAncestorNode = getAncestorNode(path, "value-func"); + const insideInFunction = + functionAncestorNode && functionAncestorNode.value; + const insideURLFunction = + insideInFunction && functionAncestorNode.value.toLowerCase() === "url"; + let didBreak = false; - for (let i = 0; i < n.groups.length; ++i) { + for (let i = 0; i < node.groups.length; ++i) { parts.push(printed[i]); + + // Ignore value inside `url()` + if (insideURLFunction) { + continue; + } + + const iPrevNode = node.groups[i - 1]; + const iNode = node.groups[i]; + const iNextNode = node.groups[i + 1]; + const iNextNextNode = node.groups[i + 2]; + + // Ignore after latest node (i.e. before semicolon) + if (!iNextNode) { + continue; + } + + // Ignore colon + if (iNode.value === ":") { + continue; + } + + // Ignore `@` in Less (i.e. `@@var;`) + if (iNode.type === "value-atword" && iNode.value === "") { + continue; + } + + // Ignore `~` in Less (i.e. `content: ~"^//* some horrible but needed css hack";`) + if (iNode.value === "~") { + continue; + } + if ( - i !== n.groups.length - 1 && - n.groups[i + 1].raws && - n.groups[i + 1].raws.before !== "" + (iPrevNode && + iPrevNode.type === "value-comment" && + iPrevNode.inline) || + (iNextNode.type === "value-comment" && iNextNode.inline) ) { - if (isGridValue) { - if ( - n.groups[i].source.start.line !== - n.groups[i + 1].source.start.line - ) { - parts.push(hardline); - didBreak = true; - } else { - parts.push(" "); - } - } else if ( - n.groups[i + 1].type === "value-operator" && - ["+", "-", "/", "*", "%"].indexOf(n.groups[i + 1].value) !== -1 + continue; + } + + const isHash = iNode.type === "value-word" && iNode.value === "#"; + const isLeftCurlyBrace = + iNode.type === "value-word" && iNode.value === "{"; + const isNextLeftCurlyBrace = + iNextNode.type === "value-word" && iNextNode.value === "{"; + const isRightCurlyBrace = + iNode.type === "value-word" && iNode.value === "}"; + const isNextRightCurlyBrace = + iNextNode.type === "value-word" && iNextNode.value === "}"; + + // Ignore interpolation in SCSS (i.e. ``#{variable}``) + if ( + isHash || + isLeftCurlyBrace || + isNextRightCurlyBrace || + (isNextLeftCurlyBrace && + iNextNode.raws && + iNextNode.raws.before === "") || + (isRightCurlyBrace && iNextNode.raws && iNextNode.raws.before === "") + ) { + continue; + } + + const isNextHash = + iNextNode.type === "value-word" && iNextNode.value === "#"; + + const isMathOperator = isMathOperatorNode(iNode); + const isNextMathOperator = isMathOperatorNode(iNextNode); + + const isMultiplication = + !isNextHash && isMathOperator && iNode.value === "*"; + const isNextMultiplication = + !isRightCurlyBrace && isNextMathOperator && iNextNode.value === "*"; + + const isDivision = !isNextHash && isMathOperator && iNode.value === "/"; + const isNextDivision = + !isRightCurlyBrace && isNextMathOperator && iNextNode.value === "/"; + + const isAddition = !isNextHash && isMathOperator && iNode.value === "+"; + const isNextAddition = + !isRightCurlyBrace && isNextMathOperator && iNextNode.value === "+"; + + const isPrevFunction = iPrevNode && iPrevNode.type === "value-func"; + const isFunction = iNode.type === "value-func"; + const isNextFunction = iNextNode.type === "value-func"; + const isNextNextFunction = + iNextNextNode && iNextNextNode.type === "value-func"; + + const isPrevWord = + iPrevNode && + ["value-word", "value-atword"].indexOf(iPrevNode.type) !== -1; + const isWord = + ["value-word", "value-atword"].indexOf(iNode.type) !== -1; + const isNextWord = + ["value-word", "value-atword"].indexOf(iNextNode.type) !== -1; + const isNextNextWord = + iNextNextNode && + ["value-word", "value-atword"].indexOf(iNextNextNode.type) !== -1; + + // Math operators + const insideCalcFunction = + insideInFunction && + functionAncestorNode.value.toLowerCase() === "calc"; + + const hasSpaceBeforeOperator = + isNextNextFunction || isNextNextWord || isFunction || isWord; + + const hasSpaceAfterOperator = + isNextFunction || isNextWord || isPrevFunction || isPrevWord; + + if ( + (isMathOperator || isNextMathOperator) && + // Multiplication + !isMultiplication && + !isNextMultiplication && + // Division + !(isNextDivision && (hasSpaceBeforeOperator || insideCalcFunction)) && + !(isDivision && (hasSpaceAfterOperator || insideCalcFunction)) && + // Addition + !(isNextAddition && hasSpaceBeforeOperator) && + !(isAddition && hasSpaceAfterOperator) + ) { + const isNextParenGroup = isParenGroupNode(iNextNode); + const isNextValueNumber = iNextNode.type === "value-number"; + + if ( + (iNextNode.raws && iNextNode.raws.before === "") || + (isMathOperator && + (isNextParenGroup || + isNextWord || + isNextValueNumber || + isMathOperatorNode(iNextNode)) && + (!iPrevNode || (iPrevNode && isMathOperatorNode(iPrevNode)))) ) { - parts.push(" "); - } else { - parts.push(line); + continue; } } + + const isEqualityOperator = + isControlDirective && isEqualityOperatorNode(iNode); + const isRelationalOperator = + isControlDirective && isRelationalOperatorNode(iNode); + const isNextEqualityOperator = + isControlDirective && isEqualityOperatorNode(iNextNode); + const isNextRelationalOperator = + isControlDirective && isRelationalOperatorNode(iNextNode); + const isNextIfElseKeyword = + isControlDirective && isIfElseKeywordNode(iNextNode); + const isEachKeyword = isControlDirective && isEachKeywordNode(iNode); + const isNextEachKeyword = + isControlDirective && isEachKeywordNode(iNextNode); + const isForKeyword = + atRuleAncestorNode && + atRuleAncestorNode.name === "for" && + isForKeywordNode(iNode); + const isNextForKeyword = + isControlDirective && isForKeywordNode(iNextNode); + const IsNextColon = iNextNode.value === ":"; + + if (isGridValue) { + if (iNode.source.start.line !== iNextNode.source.start.line) { + parts.push(hardline); + didBreak = true; + } else { + parts.push(" "); + } + } else if (iNode.type === "value-comment" && iNode.inline) { + parts.push(hardline); + } else if ( + isNextMathOperator || + isNextEqualityOperator || + isNextRelationalOperator || + isNextIfElseKeyword || + isForKeyword || + isEachKeyword + ) { + parts.push(" "); + } else if ( + !IsNextColon || + isEqualityOperator || + isRelationalOperator || + isNextForKeyword || + isNextEachKeyword + ) { + parts.push(line); + } } if (didBreak) { parts.unshift(hardline); } + if (isControlDirective) { + return group(indent(concat(parts))); + } + return group(indent(fill(parts))); } case "value-paren_group": { - const parent = path.getParentNode(); + const parentNode = path.getParentNode(); const isURLCall = - parent && parent.type === "value-func" && parent.value === "url"; + parentNode && + parentNode.type === "value-func" && + parentNode.value === "url"; if ( isURLCall && - (n.groups.length === 1 || - (n.groups.length > 0 && - n.groups[0].type === "value-comma_group" && - n.groups[0].groups.length > 0 && - n.groups[0].groups[0].type === "value-word" && - n.groups[0].groups[0].value === "data")) + (node.groups.length === 1 || + (node.groups.length > 0 && + node.groups[0].type === "value-comma_group" && + node.groups[0].groups.length > 0 && + node.groups[0].groups[0].type === "value-word" && + node.groups[0].groups[0].value.startsWith("data:"))) ) { return concat([ - n.open ? path.call(print, "open") : "", + node.open ? path.call(print, "open") : "", join(",", path.map(print, "groups")), - n.close ? path.call(print, "close") : "" + node.close ? path.call(print, "close") : "" ]); } - if (!n.open) { + if (!node.open) { const printed = path.map(print, "groups"); const res = []; @@ -364,15 +651,15 @@ function genericPrint(path, options, print) { return group(indent(fill(res))); } - const declaration = path.getParentNode(2); + const declNode = path.getParentNode(2); const isMap = - declaration && - declaration.type === "css-decl" && - declaration.prop.startsWith("$"); + declNode && + declNode.type === "css-decl" && + declNode.prop.startsWith("$"); return group( concat([ - n.open ? path.call(print, "open") : "", + node.open ? path.call(print, "open") : "", indent( concat([ softline, @@ -383,7 +670,7 @@ function genericPrint(path, options, print) { ]) ), softline, - n.close ? path.call(print, "close") : "" + node.close ? path.call(print, "close") : "" ]) ); } @@ -391,45 +678,182 @@ function genericPrint(path, options, print) { return path.call(print, "group"); } case "value-func": { - return concat([n.value, path.call(print, "group")]); + return concat([node.value, path.call(print, "group")]); } case "value-paren": { - if (n.raws.before !== "") { - return concat([line, n.value]); - } - return n.value; + return node.value; } case "value-number": { - return concat([printNumber(n.value), maybeToLowerCase(n.unit)]); + return concat([printNumber(node.value), maybeToLowerCase(node.unit)]); } case "value-operator": { - return n.value; + return node.value; } case "value-word": { - if (n.isColor && n.isHex) { - return n.value.toLowerCase(); + if ((node.isColor && node.isHex) || isWideKeywords(node.value)) { + return node.value.toLowerCase(); } - return n.value; + return node.value; } case "value-colon": { - return n.value; + return concat([node.value, insideURLFunctionNode(path) ? "" : line]); } case "value-comma": { - return concat([n.value, " "]); + return concat([node.value, " "]); } case "value-string": { - return util.printString(n.raws.quote + n.value + n.raws.quote, options); + return privateUtil.printString( + node.raws.quote + node.value + node.raws.quote, + options + ); } case "value-atword": { - return concat(["@", n.value]); + return concat(["@", node.value]); + } + case "value-unicode-range": { + return node.value; } - default: /* istanbul ignore next */ - throw new Error("unknown postcss type: " + JSON.stringify(n.type)); + throw new Error(`Unknown postcss type ${JSON.stringify(node.type)}`); } } +function isLastNode(path, node) { + const parentNode = path.getParentNode(); + if (!parentNode) { + return false; + } + const nodes = parentNode.nodes; + return nodes && nodes.indexOf(node) === nodes.length - 1; +} + +function isDetachedRulesetDeclaration(node) { + // If a Less file ends up being parsed with the SCSS parser, Less + // variable declarations will be parsed as atrules with names ending + // with a colon, so keep the original case then. + return ( + node.selector && + node.selector.type !== "selector-root-invalid" && + ((typeof node.selector === "string" && /^@.+:.*$/.test(node.selector)) || + (node.selector.value && /^@.+:.*$/.test(node.selector.value))) + ); +} + +function isKeyframeAtRuleKeywords(path, value) { + const atRuleAncestorNode = getAncestorNode(path, "css-atrule"); + return ( + atRuleAncestorNode && + atRuleAncestorNode.name && + atRuleAncestorNode.name.toLowerCase().endsWith("keyframes") && + ["from", "to"].indexOf(value.toLowerCase()) !== -1 + ); +} + +function isHTMLTag(value) { + return htmlTagNames.indexOf(value.toLowerCase()) !== -1; +} + +function insideURLFunctionNode(path) { + const funcAncestorNode = getAncestorNode(path, "value-func"); + return ( + funcAncestorNode && + funcAncestorNode.value && + funcAncestorNode.value === "url" + ); +} + +function isParenGroupNode(node) { + return node.type && node.type === "value-paren_group"; +} + +function isForKeywordNode(node) { + return ( + node.type && + node.type === "value-word" && + node.value && + ["from", "through", "end"].indexOf(node.value) !== -1 + ); +} + +function isIfElseKeywordNode(node) { + return ( + node.type && + node.type === "value-word" && + node.value && + ["and", "or", "not"].indexOf(node.value) !== -1 + ); +} + +function isEachKeywordNode(node) { + return ( + node.type && + node.type === "value-word" && + node.value && + ["in"].indexOf(node.value) !== -1 + ); +} + +function isMathOperatorNode(node) { + return ( + node.type && + node.type === "value-operator" && + node.value && + ["+", "-", "/", "*", "%"].indexOf(node.value) !== -1 + ); +} + +function isEqualityOperatorNode(node) { + return ( + node.type && + node.type === "value-word" && + node.value && + ["==", "!="].indexOf(node.value) !== -1 + ); +} + +function isRelationalOperatorNode(node) { + return ( + node.type && + node.type === "value-word" && + node.value && + ["<", ">", "<=", ">="].indexOf(node.value) !== -1 + ); +} + +function isControlDirectiveNode(node) { + return ( + node.type && + node.type === "css-atrule" && + node.name && + (node.name === "if" || + node.name === "else" || + node.name === "for" || + node.name === "each" || + node.name === "while") + ); +} + +function getAncestorCounter(path, typeOrTypes) { + const types = [].concat(typeOrTypes); + + let counter = -1; + let ancestorNode; + + while ((ancestorNode = path.getParentNode(++counter))) { + if (types.indexOf(ancestorNode.type) !== -1) { + return counter; + } + } + + return -1; +} + +function getAncestorNode(path, typeOrTypes) { + const counter = getAncestorCounter(path, typeOrTypes); + return counter === -1 ? null : path.getParentNode(counter); +} + function printNodeSequence(path, options, print) { const node = path.getValue(); const parts = []; @@ -444,8 +868,8 @@ function printNodeSequence(path, options, print) { const childNode = pathChild.getValue(); parts.push( options.originalText.slice( - util.locStart(childNode), - util.locEnd(childNode) + options.locStart(childNode), + options.locEnd(childNode) ) ); } else { @@ -455,9 +879,9 @@ function printNodeSequence(path, options, print) { if (i !== node.nodes.length - 1) { if ( (node.nodes[i + 1].type === "css-comment" && - !util.hasNewline( + !privateUtil.hasNewline( options.originalText, - util.locStart(node.nodes[i + 1]), + options.locStart(node.nodes[i + 1]), { backwards: true } )) || (node.nodes[i + 1].type === "css-atrule" && @@ -467,7 +891,13 @@ function printNodeSequence(path, options, print) { parts.push(" "); } else { parts.push(hardline); - if (util.isNextLineEmpty(options.originalText, pathChild.getValue())) { + if ( + sharedUtil.isNextLineEmpty( + options.originalText, + pathChild.getValue(), + options + ) + ) { parts.push(hardline); } } @@ -478,10 +908,6 @@ function printNodeSequence(path, options, print) { return concat(parts); } -function printValue(value) { - return value; -} - const STRING_REGEX = /(['"])(?:(?!\1)[^\\]|\\[\s\S])*\1/g; const NUMBER_REGEX = /(?:\d*\.\d+|\d+\.?)(?:[eE][+-]?\d+)?/g; const STANDARD_UNIT_REGEX = /[a-zA-Z]+/g; @@ -496,7 +922,9 @@ const ADJUST_NUMBERS_REGEX = RegExp( ); function adjustStrings(value, options) { - return value.replace(STRING_REGEX, match => util.printString(match, options)); + return value.replace(STRING_REGEX, match => + privateUtil.printString(match, options) + ); } function quoteAttributeValue(value, options) { @@ -518,7 +946,7 @@ function adjustNumbers(value) { function printNumber(rawNumber) { return ( - util + privateUtil .printNumber(rawNumber) // Remove trailing `.0`. .replace(/\.0(?=$|e)/, "") @@ -530,14 +958,23 @@ function maybeToLowerCase(value) { value.includes("@") || value.includes("#") || value.startsWith("%") || - value.startsWith("--") + value.startsWith("--") || + value.startsWith(":--") || + (value.includes("(") && value.includes(")")) ? value : value.toLowerCase(); } +function isWideKeywords(value) { + return ( + ["initial", "inherit", "unset", "revert"].indexOf( + value.replace().toLowerCase() + ) !== -1 + ); +} + module.exports = { - options: printerOptions, print: genericPrint, - hasPrettierIgnore: util.hasIgnoreComment, + hasPrettierIgnore: privateUtil.hasIgnoreComment, massageAstNode: clean }; diff --git a/src/language-graphql/index.js b/src/language-graphql/index.js index 194f7e5c..24b05ebd 100644 --- a/src/language-graphql/index.js +++ b/src/language-graphql/index.js @@ -1,6 +1,7 @@ "use strict"; const printer = require("./printer-graphql"); +const options = require("./options"); // Based on: // https://github.com/github/linguist/blob/master/lib/linguist/languages.yml @@ -23,7 +24,19 @@ const parsers = { get parse() { return eval("require")("./parser-graphql"); }, - astFormat: "graphql" + astFormat: "graphql", + locStart: function(node) { + if (typeof node.start === "number") { + return node.start; + } + return node.loc && node.loc.start; + }, + locEnd: function(node) { + if (typeof node.end === "number") { + return node.end; + } + return node.loc && node.loc.end; + } } }; @@ -33,6 +46,7 @@ const printers = { module.exports = { languages, + options, parsers, printers }; diff --git a/src/language-graphql/parser-graphql.js b/src/language-graphql/parser-graphql.js index d72e9b36..7addec77 100644 --- a/src/language-graphql/parser-graphql.js +++ b/src/language-graphql/parser-graphql.js @@ -34,11 +34,19 @@ function removeTokens(node) { return node; } +function fallbackParser(parse, source) { + try { + return parse(source, { allowLegacySDLImplementsInterfaces: false }); + } catch (_) { + return parse(source, { allowLegacySDLImplementsInterfaces: true }); + } +} + function parse(text /*, parsers, opts*/) { // Inline the require to avoid loading all the JS if we don't use it const parser = require("graphql/language"); try { - const ast = parser.parse(text); + const ast = fallbackParser(parser.parse, text); ast.comments = parseComments(ast); removeTokens(ast); return ast; diff --git a/src/language-graphql/printer-graphql.js b/src/language-graphql/printer-graphql.js index 94b953fb..1a43e1db 100644 --- a/src/language-graphql/printer-graphql.js +++ b/src/language-graphql/printer-graphql.js @@ -9,9 +9,8 @@ const softline = docBuilders.softline; const group = docBuilders.group; const indent = docBuilders.indent; const ifBreak = docBuilders.ifBreak; -const printerOptions = require("./options"); - -const util = require("../common/util"); +const privateUtil = require("../common/util"); +const sharedUtil = require("../common/util-shared"); function genericPrint(path, options, print) { const n = path.getValue(); @@ -31,9 +30,11 @@ function genericPrint(path, options, print) { ]); } case "OperationDefinition": { + const hasOperation = options.originalText[options.locStart(n)] !== "{"; + const hasName = !!n.name; return concat([ - n.name === null ? "" : n.operation, - n.name ? concat([" ", path.call(print, "name")]) : "", + hasOperation ? n.operation : "", + hasOperation && hasName ? concat([" ", path.call(print, "name")]) : "", n.variableDefinitions && n.variableDefinitions.length ? group( concat([ @@ -53,7 +54,7 @@ function genericPrint(path, options, print) { ) : "", printDirectives(path, print, n), - n.selectionSet ? (n.name === null ? "" : " ") : "", + n.selectionSet ? (!hasOperation && !hasName ? "" : " ") : "", path.call(print, "selectionSet") ]); } @@ -123,6 +124,15 @@ function genericPrint(path, options, print) { return n.value; } case "StringValue": { + if (n.block) { + return concat([ + '"""', + hardline, + join(hardline, n.value.replace(/"""/g, "\\$&").split("\n")), + hardline, + '"""' + ]); + } return concat(['"', n.value.replace(/["\\]/g, "\\$&"), '"']); } case "IntValue": @@ -231,36 +241,55 @@ function genericPrint(path, options, print) { return concat(["extend ", path.call(print, "definition")]); } + case "ObjectTypeExtension": case "ObjectTypeDefinition": { return concat([ + path.call(print, "description"), + n.description ? hardline : "", + n.kind === "ObjectTypeExtension" ? "extend " : "", "type ", path.call(print, "name"), n.interfaces.length > 0 - ? concat([" implements ", join(", ", path.map(print, "interfaces"))]) + ? concat([ + " implements ", + join( + determineInterfaceSeparator( + options.originalText.substr( + options.locStart(n), + options.locEnd(n) + ) + ), + path.map(print, "interfaces") + ) + ]) : "", printDirectives(path, print, n), - " {", n.fields.length > 0 - ? indent( - concat([ - hardline, - join( + ? concat([ + " {", + indent( + concat([ hardline, - path.call( - fieldsPath => printSequence(fieldsPath, options, print), - "fields" + join( + hardline, + path.call( + fieldsPath => printSequence(fieldsPath, options, print), + "fields" + ) ) - ) - ]) - ) - : "", - hardline, - "}" + ]) + ), + hardline, + "}" + ]) + : "" ]); } case "FieldDefinition": { return concat([ + path.call(print, "description"), + n.description ? hardline : "", path.call(print, "name"), n.arguments.length > 0 ? group( @@ -291,6 +320,8 @@ function genericPrint(path, options, print) { case "DirectiveDefinition": { return concat([ + path.call(print, "description"), + n.description ? hardline : "", "directive ", "@", path.call(print, "name"), @@ -319,33 +350,42 @@ function genericPrint(path, options, print) { ]); } + case "EnumTypeExtension": case "EnumTypeDefinition": { return concat([ + path.call(print, "description"), + n.description ? hardline : "", + n.kind === "EnumTypeExtension" ? "extend " : "", "enum ", path.call(print, "name"), printDirectives(path, print, n), - " {", + n.values.length > 0 - ? indent( - concat([ - hardline, - join( + ? concat([ + " {", + indent( + concat([ hardline, - path.call( - valuesPath => printSequence(valuesPath, options, print), - "values" + join( + hardline, + path.call( + valuesPath => printSequence(valuesPath, options, print), + "values" + ) ) - ) - ]) - ) - : "", - hardline, - "}" + ]) + ), + hardline, + "}" + ]) + : "" ]); } case "EnumValueDefinition": { return concat([ + path.call(print, "description"), + n.description ? hardline : "", path.call(print, "name"), printDirectives(path, print, n) ]); @@ -353,6 +393,8 @@ function genericPrint(path, options, print) { case "InputValueDefinition": { return concat([ + path.call(print, "description"), + n.description ? (n.description.block ? hardline : line) : "", path.call(print, "name"), ": ", path.call(print, "type"), @@ -361,28 +403,34 @@ function genericPrint(path, options, print) { ]); } + case "InputObjectTypeExtension": case "InputObjectTypeDefinition": { return concat([ + path.call(print, "description"), + n.description ? hardline : "", + n.kind === "InputObjectTypeExtension" ? "extend " : "", "input ", path.call(print, "name"), printDirectives(path, print, n), - " {", n.fields.length > 0 - ? indent( - concat([ - hardline, - join( + ? concat([ + " {", + indent( + concat([ hardline, - path.call( - fieldsPath => printSequence(fieldsPath, options, print), - "fields" + join( + hardline, + path.call( + fieldsPath => printSequence(fieldsPath, options, print), + "fields" + ) ) - ) - ]) - ) - : "", - hardline, - "}" + ]) + ), + hardline, + "}" + ]) + : "" ]); } @@ -418,28 +466,35 @@ function genericPrint(path, options, print) { ]); } + case "InterfaceTypeExtension": case "InterfaceTypeDefinition": { return concat([ + path.call(print, "description"), + n.description ? hardline : "", + n.kind === "InterfaceTypeExtension" ? "extend " : "", "interface ", path.call(print, "name"), printDirectives(path, print, n), - " {", + n.fields.length > 0 - ? indent( - concat([ - hardline, - join( + ? concat([ + " {", + indent( + concat([ hardline, - path.call( - fieldsPath => printSequence(fieldsPath, options, print), - "fields" + join( + hardline, + path.call( + fieldsPath => printSequence(fieldsPath, options, print), + "fields" + ) ) - ) - ]) - ) - : "", - hardline, - "}" + ]) + ), + hardline, + "}" + ]) + : "" ]); } @@ -463,25 +518,42 @@ function genericPrint(path, options, print) { ]); } + case "UnionTypeExtension": case "UnionTypeDefinition": { return group( concat([ - "union ", - path.call(print, "name"), - " =", - ifBreak("", " "), - indent( + path.call(print, "description"), + n.description ? hardline : "", + group( concat([ - ifBreak(concat([line, " "])), - join(concat([line, "| "]), path.map(print, "types")) + n.kind === "UnionTypeExtension" ? "extend " : "", + "union ", + path.call(print, "name"), + printDirectives(path, print, n), + n.types.length > 0 + ? concat([ + " =", + ifBreak("", " "), + indent( + concat([ + ifBreak(concat([line, " "])), + join(concat([line, "| "]), path.map(print, "types")) + ]) + ) + ]) + : "" ]) ) ]) ); } + case "ScalarTypeExtension": case "ScalarTypeDefinition": { return concat([ + path.call(print, "description"), + n.description ? hardline : "", + n.kind === "ScalarTypeExtension" ? "extend " : "", "scalar ", path.call(print, "name"), printDirectives(path, print, n) @@ -530,7 +602,11 @@ function printSequence(sequencePath, options, print) { const printed = print(path); if ( - util.isNextLineEmpty(options.originalText, path.getValue()) && + sharedUtil.isNextLineEmpty( + options.originalText, + path.getValue(), + options + ) && i < count - 1 ) { return concat([printed, hardline]); @@ -555,10 +631,21 @@ function printComment(commentPath) { } } +function determineInterfaceSeparator(originalSource) { + const start = originalSource.indexOf("implements"); + if (start === -1) { + throw new Error("Must implement interfaces: " + originalSource); + } + let end = originalSource.indexOf("{"); + if (end === -1) { + end = originalSource.length; + } + return originalSource.substr(start, end).includes("&") ? " & " : ", "; +} + module.exports = { - options: printerOptions, print: genericPrint, - hasPrettierIgnore: util.hasIgnoreComment, + hasPrettierIgnore: privateUtil.hasIgnoreComment, printComment, canAttachComment }; diff --git a/src/language-handlebars/index.js b/src/language-handlebars/index.js index 8278df18..d1669204 100644 --- a/src/language-handlebars/index.js +++ b/src/language-handlebars/index.js @@ -13,7 +13,8 @@ const languages = [ extensions: [".handlebars", ".hbs"], tm_scope: "text.html.handlebars", ace_mode: "handlebars", - language_id: 155 + language_id: 155, + since: null // unreleased } ]; @@ -22,7 +23,13 @@ const parsers = { get parse() { return eval("require")("./parser-glimmer"); }, - astFormat: "glimmer" + astFormat: "glimmer", + locEnd: function(node) { + return node.loc && node.loc.end; + }, + locStart: function(node) { + return node.loc && node.loc.start; + } } }; diff --git a/src/language-handlebars/printer-glimmer.js b/src/language-handlebars/printer-glimmer.js index d537aeb0..4091ff78 100644 --- a/src/language-handlebars/printer-glimmer.js +++ b/src/language-handlebars/printer-glimmer.js @@ -96,11 +96,16 @@ function print(path, options, print) { } case "BlockStatement": { const pp = path.getParentNode(1); - const isElseIf = pp && pp.inverse && pp.inverse.body[0] === n; + const isElseIf = + pp && + pp.inverse && + pp.inverse.body[0] === n && + pp.inverse.body[0].path.parts[0] === "if"; const hasElseIf = n.inverse && n.inverse.body[0] && - n.inverse.body[0].type === "BlockStatement"; + n.inverse.body[0].type === "BlockStatement" && + n.inverse.body[0].path.parts[0] === "if"; const indentElse = hasElseIf ? a => a : indent; if (n.inverse) { return concat([ @@ -114,6 +119,11 @@ function print(path, options, print) { : "", isElseIf ? "" : concat([hardline, printCloseBlock(path, print)]) ]); + } else if (isElseIf) { + return concat([ + concat(["{{else ", printPathParams(path, print), "}}"]), + indent(concat([hardline, path.call(print, "program")])) + ]); } /** * I want this boolean to be: if params are going to cause a break, diff --git a/src/language-html/embed.js b/src/language-html/embed.js index 8c1f7a6e..c07a6e97 100644 --- a/src/language-html/embed.js +++ b/src/language-html/embed.js @@ -1,6 +1,6 @@ "use strict"; -const util = require("../common/util"); +const privateUtil = require("../common/util"); const doc = require("../doc"); const docUtils = doc.utils; const docBuilders = doc.builders; @@ -66,7 +66,7 @@ function embed(path, print, textToDoc, options) { return concat([ node.key, '="', - util.hasNewlineInRange(node.value, 0, node.value.length) + privateUtil.hasNewlineInRange(node.value, 0, node.value.length) ? doc : docUtils.removeLines(doc), '"' @@ -87,7 +87,10 @@ function parseJavaScriptExpression(text, parsers) { } function getText(options, node) { - return options.originalText.slice(util.locStart(node), util.locEnd(node)); + return options.originalText.slice( + options.locStart(node), + options.locEnd(node) + ); } module.exports = embed; diff --git a/src/language-html/index.js b/src/language-html/index.js index 0842539d..05ae371b 100644 --- a/src/language-html/index.js +++ b/src/language-html/index.js @@ -8,7 +8,7 @@ const printer = require("./printer-htmlparser2"); const languages = [ { name: "HTML", - since: undefined, // unreleased + since: null, // unreleased parsers: ["parse5"], group: "HTML", tmScope: "text.html.basic", @@ -27,7 +27,13 @@ const parsers = { get parse() { return eval("require")("./parser-parse5"); }, - astFormat: "htmlparser2" + astFormat: "htmlparser2", + locEnd: function(node) { + return node.__location && node.__location.endOffset; + }, + locStart: function(node) { + return node.__location && node.__location.startOffset; + } } }; diff --git a/src/language-html/parser-parse5.js b/src/language-html/parser-parse5.js index eee3eacd..98dca479 100644 --- a/src/language-html/parser-parse5.js +++ b/src/language-html/parser-parse5.js @@ -1,26 +1,17 @@ "use strict"; -// const createError = require("./parser-create-error"); - function parse(text /*, parsers, opts*/) { // Inline the require to avoid loading all the JS if we don't use it const parse5 = require("parse5"); try { - const isFragment = !/^\s*<(!doctype|html|head|body)/i.test(text); + const isFragment = !/^\s*<(!doctype|html|head|body|!--)/i.test(text); const ast = (isFragment ? parse5.parseFragment : parse5.parse)(text, { treeAdapter: parse5.treeAdapters.htmlparser2, locationInfo: true }); return extendAst(ast); } catch (error) { - // throw createError(error.message, { - // start: { - // line: error.locations[0].line, - // column: error.locations[0].column - // } - // } else { throw error; - // } } } diff --git a/src/language-html/printer-htmlparser2.js b/src/language-html/printer-htmlparser2.js index 7a697679..3da2c65f 100644 --- a/src/language-html/printer-htmlparser2.js +++ b/src/language-html/printer-htmlparser2.js @@ -1,7 +1,7 @@ "use strict"; const embed = require("./embed"); -const util = require("../common/util"); +const privateUtil = require("../common/util"); const docBuilders = require("../doc").builders; const concat = docBuilders.concat; const join = docBuilders.join; @@ -10,7 +10,6 @@ const line = docBuilders.line; const softline = docBuilders.softline; const group = docBuilders.group; const indent = docBuilders.indent; -// const ifBreak = docBuilders.ifBreak; // http://w3c.github.io/html/single-page.html#void-elements const voidTags = { @@ -56,10 +55,10 @@ function genericPrint(path, options, print) { const selfClose = voidTags[n.name] ? ">" : " />"; const children = printChildren(path, print); - const hasNewline = util.hasNewlineInRange( + const hasNewline = privateUtil.hasNewlineInRange( options.originalText, - util.locStart(n), - util.locEnd(n) + options.locStart(n), + options.locEnd(n) ); return group( @@ -118,5 +117,5 @@ function printChildren(path, print) { module.exports = { print: genericPrint, embed, - hasPrettierIgnore: util.hasIgnoreComment + hasPrettierIgnore: privateUtil.hasIgnoreComment }; diff --git a/src/language-js/comments.js b/src/language-js/comments.js new file mode 100644 index 00000000..a8c2993c --- /dev/null +++ b/src/language-js/comments.js @@ -0,0 +1,707 @@ +"use strict"; + +const privateUtil = require("../common/util"); +const sharedUtil = require("../common/util-shared"); + +const addLeadingComment = sharedUtil.addLeadingComment; +const addTrailingComment = sharedUtil.addTrailingComment; +const addDanglingComment = sharedUtil.addDanglingComment; + +function handleOwnLineComment(comment, text, options, ast, isLastComment) { + const precedingNode = comment.precedingNode; + const enclosingNode = comment.enclosingNode; + const followingNode = comment.followingNode; + if ( + handleLastFunctionArgComments( + text, + precedingNode, + enclosingNode, + followingNode, + comment, + options + ) || + handleMemberExpressionComments(enclosingNode, followingNode, comment) || + handleIfStatementComments( + text, + precedingNode, + enclosingNode, + followingNode, + comment, + options + ) || + handleTryStatementComments(enclosingNode, followingNode, comment) || + handleClassComments(enclosingNode, precedingNode, followingNode, comment) || + handleImportSpecifierComments(enclosingNode, comment) || + handleForComments(enclosingNode, precedingNode, comment) || + handleUnionTypeComments( + precedingNode, + enclosingNode, + followingNode, + comment + ) || + handleOnlyComments(enclosingNode, ast, comment, isLastComment) || + handleImportDeclarationComments( + text, + enclosingNode, + precedingNode, + comment, + options + ) || + handleAssignmentPatternComments(enclosingNode, comment) || + handleMethodNameComments( + text, + enclosingNode, + precedingNode, + comment, + options + ) + ) { + return true; + } + return false; +} + +function handleEndOfLineComment(comment, text, options, ast, isLastComment) { + const precedingNode = comment.precedingNode; + const enclosingNode = comment.enclosingNode; + const followingNode = comment.followingNode; + if ( + handleLastFunctionArgComments( + text, + precedingNode, + enclosingNode, + followingNode, + comment, + options + ) || + handleConditionalExpressionComments( + enclosingNode, + precedingNode, + followingNode, + comment, + text, + options + ) || + handleImportSpecifierComments(enclosingNode, comment) || + handleIfStatementComments( + text, + precedingNode, + enclosingNode, + followingNode, + comment, + options + ) || + handleClassComments(enclosingNode, precedingNode, followingNode, comment) || + handleLabeledStatementComments(enclosingNode, comment) || + handleCallExpressionComments(precedingNode, enclosingNode, comment) || + handlePropertyComments(enclosingNode, comment) || + handleOnlyComments(enclosingNode, ast, comment, isLastComment) || + handleTypeAliasComments(enclosingNode, followingNode, comment) || + handleVariableDeclaratorComments(enclosingNode, followingNode, comment) + ) { + return true; + } + return false; +} + +function handleRemainingComment(comment, text, options, ast, isLastComment) { + const precedingNode = comment.precedingNode; + const enclosingNode = comment.enclosingNode; + const followingNode = comment.followingNode; + if ( + handleIfStatementComments( + text, + precedingNode, + enclosingNode, + followingNode, + comment, + options + ) || + handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) || + handleCommentInEmptyParens(text, enclosingNode, comment, options) || + handleMethodNameComments( + text, + enclosingNode, + precedingNode, + comment, + options + ) || + handleOnlyComments(enclosingNode, ast, comment, isLastComment) || + handleCommentAfterArrowParams(text, enclosingNode, comment, options) || + handleFunctionNameComments( + text, + enclosingNode, + precedingNode, + comment, + options + ) || + handleBreakAndContinueStatementComments(enclosingNode, comment) + ) { + return true; + } + return false; +} + +function addBlockStatementFirstComment(node, comment) { + const body = node.body.filter(n => n.type !== "EmptyStatement"); + if (body.length === 0) { + addDanglingComment(node, comment); + } else { + addLeadingComment(body[0], comment); + } +} + +function addBlockOrNotComment(node, comment) { + if (node.type === "BlockStatement") { + addBlockStatementFirstComment(node, comment); + } else { + addLeadingComment(node, comment); + } +} + +// There are often comments before the else clause of if statements like +// +// if (1) { ... } +// // comment +// else { ... } +// +// They are being attached as leading comments of the BlockExpression which +// is not well printed. What we want is to instead move the comment inside +// of the block and make it leadingComment of the first element of the block +// or dangling comment of the block if there is nothing inside +// +// if (1) { ... } +// else { +// // comment +// ... +// } +function handleIfStatementComments( + text, + precedingNode, + enclosingNode, + followingNode, + comment, + options +) { + if ( + !enclosingNode || + enclosingNode.type !== "IfStatement" || + !followingNode + ) { + return false; + } + + // We unfortunately have no way using the AST or location of nodes to know + // if the comment is positioned before the condition parenthesis: + // if (a /* comment */) {} + // The only workaround I found is to look at the next character to see if + // it is a ). + const nextCharacter = privateUtil.getNextNonSpaceNonCommentCharacter( + text, + comment, + options.locEnd + ); + if (nextCharacter === ")") { + addTrailingComment(precedingNode, comment); + return true; + } + + // Comments before `else`: + // - treat as trailing comments of the consequent, if it's a BlockStatement + // - treat as a dangling comment otherwise + if ( + precedingNode === enclosingNode.consequent && + followingNode === enclosingNode.alternate + ) { + if (precedingNode.type === "BlockStatement") { + addTrailingComment(precedingNode, comment); + } else { + addDanglingComment(enclosingNode, comment); + } + return true; + } + + if (followingNode.type === "BlockStatement") { + addBlockStatementFirstComment(followingNode, comment); + return true; + } + + if (followingNode.type === "IfStatement") { + addBlockOrNotComment(followingNode.consequent, comment); + return true; + } + + // For comments positioned after the condition parenthesis in an if statement + // before the consequent with or without brackets on, such as + // if (a) /* comment */ {} or if (a) /* comment */ true, + // we look at the next character to see if it is a { or if the following node + // is the consequent for the if statement + if (nextCharacter === "{" || enclosingNode.consequent === followingNode) { + addLeadingComment(followingNode, comment); + return true; + } + + return false; +} + +// Same as IfStatement but for TryStatement +function handleTryStatementComments(enclosingNode, followingNode, comment) { + if ( + !enclosingNode || + enclosingNode.type !== "TryStatement" || + !followingNode + ) { + return false; + } + + if (followingNode.type === "BlockStatement") { + addBlockStatementFirstComment(followingNode, comment); + return true; + } + + if (followingNode.type === "TryStatement") { + addBlockOrNotComment(followingNode.finalizer, comment); + return true; + } + + if (followingNode.type === "CatchClause") { + addBlockOrNotComment(followingNode.body, comment); + return true; + } + + return false; +} + +function handleMemberExpressionComments(enclosingNode, followingNode, comment) { + if ( + enclosingNode && + enclosingNode.type === "MemberExpression" && + followingNode && + followingNode.type === "Identifier" + ) { + addLeadingComment(enclosingNode, comment); + return true; + } + + return false; +} + +function handleConditionalExpressionComments( + enclosingNode, + precedingNode, + followingNode, + comment, + text, + options +) { + const isSameLineAsPrecedingNode = + precedingNode && + !privateUtil.hasNewlineInRange( + text, + options.locEnd(precedingNode), + options.locStart(comment) + ); + + if ( + (!precedingNode || !isSameLineAsPrecedingNode) && + enclosingNode && + enclosingNode.type === "ConditionalExpression" && + followingNode + ) { + addLeadingComment(followingNode, comment); + return true; + } + return false; +} + +function handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) { + if ( + enclosingNode && + (enclosingNode.type === "ObjectProperty" || + enclosingNode.type === "Property") && + enclosingNode.shorthand && + enclosingNode.key === precedingNode && + enclosingNode.value.type === "AssignmentPattern" + ) { + addTrailingComment(enclosingNode.value.left, comment); + return true; + } + return false; +} + +function handleClassComments( + enclosingNode, + precedingNode, + followingNode, + comment +) { + if ( + enclosingNode && + (enclosingNode.type === "ClassDeclaration" || + enclosingNode.type === "ClassExpression") && + (enclosingNode.decorators && enclosingNode.decorators.length > 0) && + !(followingNode && followingNode.type === "Decorator") + ) { + if (!enclosingNode.decorators || enclosingNode.decorators.length === 0) { + addLeadingComment(enclosingNode, comment); + } else { + addTrailingComment( + enclosingNode.decorators[enclosingNode.decorators.length - 1], + comment + ); + } + return true; + } + return false; +} + +function handleMethodNameComments( + text, + enclosingNode, + precedingNode, + comment, + options +) { + // This is only needed for estree parsers (flow, typescript) to attach + // after a method name: + // obj = { fn /*comment*/() {} }; + if ( + enclosingNode && + precedingNode && + (enclosingNode.type === "Property" || + enclosingNode.type === "MethodDefinition") && + precedingNode.type === "Identifier" && + enclosingNode.key === precedingNode && + // special Property case: { key: /*comment*/(value) }; + // comment should be attached to value instead of key + privateUtil.getNextNonSpaceNonCommentCharacter( + text, + precedingNode, + options.locEnd + ) !== ":" + ) { + addTrailingComment(precedingNode, comment); + return true; + } + + // Print comments between decorators and class methods as a trailing comment + // on the decorator node instead of the method node + if ( + precedingNode && + enclosingNode && + precedingNode.type === "Decorator" && + (enclosingNode.type === "ClassMethod" || + enclosingNode.type === "ClassProperty" || + enclosingNode.type === "TSAbstractClassProperty" || + enclosingNode.type === "TSAbstractMethodDefinition" || + enclosingNode.type === "MethodDefinition") + ) { + addTrailingComment(precedingNode, comment); + return true; + } + + return false; +} + +function handleFunctionNameComments( + text, + enclosingNode, + precedingNode, + comment, + options +) { + if ( + privateUtil.getNextNonSpaceNonCommentCharacter( + text, + comment, + options.locEnd + ) !== "(" + ) { + return false; + } + + if ( + precedingNode && + enclosingNode && + (enclosingNode.type === "FunctionDeclaration" || + enclosingNode.type === "FunctionExpression" || + enclosingNode.type === "ClassMethod" || + enclosingNode.type === "MethodDefinition" || + enclosingNode.type === "ObjectMethod") + ) { + addTrailingComment(precedingNode, comment); + return true; + } + return false; +} + +function handleCommentAfterArrowParams(text, enclosingNode, comment, options) { + if (!(enclosingNode && enclosingNode.type === "ArrowFunctionExpression")) { + return false; + } + + const index = sharedUtil.getNextNonSpaceNonCommentCharacterIndex( + text, + comment, + options + ); + if (text.substr(index, 2) === "=>") { + addDanglingComment(enclosingNode, comment); + return true; + } + + return false; +} + +function handleCommentInEmptyParens(text, enclosingNode, comment, options) { + if ( + privateUtil.getNextNonSpaceNonCommentCharacter( + text, + comment, + options.locEnd + ) !== ")" + ) { + return false; + } + + // Only add dangling comments to fix the case when no params are present, + // i.e. a function without any argument. + if ( + enclosingNode && + (((enclosingNode.type === "FunctionDeclaration" || + enclosingNode.type === "FunctionExpression" || + (enclosingNode.type === "ArrowFunctionExpression" && + (enclosingNode.body.type !== "CallExpression" || + enclosingNode.body.arguments.length === 0)) || + enclosingNode.type === "ClassMethod" || + enclosingNode.type === "ObjectMethod") && + enclosingNode.params.length === 0) || + (enclosingNode.type === "CallExpression" && + enclosingNode.arguments.length === 0)) + ) { + addDanglingComment(enclosingNode, comment); + return true; + } + if ( + enclosingNode && + (enclosingNode.type === "MethodDefinition" && + enclosingNode.value.params.length === 0) + ) { + addDanglingComment(enclosingNode.value, comment); + return true; + } + return false; +} + +function handleLastFunctionArgComments( + text, + precedingNode, + enclosingNode, + followingNode, + comment, + options +) { + // Type definitions functions + if ( + precedingNode && + precedingNode.type === "FunctionTypeParam" && + enclosingNode && + enclosingNode.type === "FunctionTypeAnnotation" && + followingNode && + followingNode.type !== "FunctionTypeParam" + ) { + addTrailingComment(precedingNode, comment); + return true; + } + + // Real functions + if ( + precedingNode && + (precedingNode.type === "Identifier" || + precedingNode.type === "AssignmentPattern") && + enclosingNode && + (enclosingNode.type === "ArrowFunctionExpression" || + enclosingNode.type === "FunctionExpression" || + enclosingNode.type === "FunctionDeclaration" || + enclosingNode.type === "ObjectMethod" || + enclosingNode.type === "ClassMethod") && + privateUtil.getNextNonSpaceNonCommentCharacter( + text, + comment, + options.locEnd + ) === ")" + ) { + addTrailingComment(precedingNode, comment); + return true; + } + return false; +} + +function handleImportSpecifierComments(enclosingNode, comment) { + if (enclosingNode && enclosingNode.type === "ImportSpecifier") { + addLeadingComment(enclosingNode, comment); + return true; + } + return false; +} + +function handleLabeledStatementComments(enclosingNode, comment) { + if (enclosingNode && enclosingNode.type === "LabeledStatement") { + addLeadingComment(enclosingNode, comment); + return true; + } + return false; +} + +function handleBreakAndContinueStatementComments(enclosingNode, comment) { + if ( + enclosingNode && + (enclosingNode.type === "ContinueStatement" || + enclosingNode.type === "BreakStatement") && + !enclosingNode.label + ) { + addTrailingComment(enclosingNode, comment); + return true; + } + return false; +} + +function handleCallExpressionComments(precedingNode, enclosingNode, comment) { + if ( + enclosingNode && + enclosingNode.type === "CallExpression" && + precedingNode && + enclosingNode.callee === precedingNode && + enclosingNode.arguments.length > 0 + ) { + addLeadingComment(enclosingNode.arguments[0], comment); + return true; + } + return false; +} + +function handleUnionTypeComments( + precedingNode, + enclosingNode, + followingNode, + comment +) { + if ( + enclosingNode && + (enclosingNode.type === "UnionTypeAnnotation" || + enclosingNode.type === "TSUnionType") + ) { + addTrailingComment(precedingNode, comment); + return true; + } + return false; +} + +function handlePropertyComments(enclosingNode, comment) { + if ( + enclosingNode && + (enclosingNode.type === "Property" || + enclosingNode.type === "ObjectProperty") + ) { + addLeadingComment(enclosingNode, comment); + return true; + } + return false; +} + +function handleOnlyComments(enclosingNode, ast, comment, isLastComment) { + // With Flow the enclosingNode is undefined so use the AST instead. + if (ast && ast.body && ast.body.length === 0) { + if (isLastComment) { + addDanglingComment(ast, comment); + } else { + addLeadingComment(ast, comment); + } + return true; + } else if ( + enclosingNode && + enclosingNode.type === "Program" && + enclosingNode.body.length === 0 && + enclosingNode.directives && + enclosingNode.directives.length === 0 + ) { + if (isLastComment) { + addDanglingComment(enclosingNode, comment); + } else { + addLeadingComment(enclosingNode, comment); + } + return true; + } + return false; +} + +function handleForComments(enclosingNode, precedingNode, comment) { + if ( + enclosingNode && + (enclosingNode.type === "ForInStatement" || + enclosingNode.type === "ForOfStatement") + ) { + addLeadingComment(enclosingNode, comment); + return true; + } + return false; +} + +function handleImportDeclarationComments( + text, + enclosingNode, + precedingNode, + comment, + options +) { + if ( + precedingNode && + enclosingNode && + enclosingNode.type === "ImportDeclaration" && + privateUtil.hasNewline(text, options.locEnd(comment)) + ) { + addTrailingComment(precedingNode, comment); + return true; + } + return false; +} + +function handleAssignmentPatternComments(enclosingNode, comment) { + if (enclosingNode && enclosingNode.type === "AssignmentPattern") { + addLeadingComment(enclosingNode, comment); + return true; + } + return false; +} + +function handleTypeAliasComments(enclosingNode, followingNode, comment) { + if (enclosingNode && enclosingNode.type === "TypeAlias") { + addLeadingComment(enclosingNode, comment); + return true; + } + return false; +} + +function handleVariableDeclaratorComments( + enclosingNode, + followingNode, + comment +) { + if ( + enclosingNode && + enclosingNode.type === "VariableDeclarator" && + followingNode && + (followingNode.type === "ObjectExpression" || + followingNode.type === "ArrayExpression") + ) { + addLeadingComment(followingNode, comment); + return true; + } + return false; +} + +module.exports = { + handleOwnLineComment, + handleEndOfLineComment, + handleRemainingComment +}; diff --git a/src/language-js/embed.js b/src/language-js/embed.js index 08d47768..04fc769c 100644 --- a/src/language-js/embed.js +++ b/src/language-js/embed.js @@ -8,7 +8,9 @@ const indent = docBuilders.indent; const join = docBuilders.join; const hardline = docBuilders.hardline; const softline = docBuilders.softline; +const literalline = docBuilders.literalline; const concat = docBuilders.concat; +const dedentToRoot = docBuilders.dedentToRoot; function embed(path, print, textToDoc /*, options */) { const node = path.getValue(); @@ -24,7 +26,16 @@ function embed(path, print, textToDoc /*, options */) { if (isCss) { // Get full template literal with expressions replaced by placeholders const rawQuasis = node.quasis.map(q => q.value.raw); - const text = rawQuasis.join("@prettier-placeholder"); + let placeholderID = 0; + const text = rawQuasis.reduce((prevVal, currVal, idx) => { + return idx == 0 + ? currVal + : prevVal + + "@prettier-placeholder-" + + placeholderID++ + + "-id" + + currVal; + }, ""); const doc = textToDoc(text, { parser: "css" }); return transformCssDoc(doc, path, print); } @@ -66,7 +77,14 @@ function embed(path, print, textToDoc /*, options */) { const templateElement = node.quasis[i]; const isFirst = i === 0; const isLast = i === numQuasis - 1; - const text = templateElement.value.raw; + const text = templateElement.value.cooked; + + // Bail out if any of the quasis have an invalid escape sequence + // (which would make the `cooked` value be `null` or `undefined`) + if (typeof text !== "string") { + return null; + } + const lines = text.split("\n"); const numLines = lines.length; const expressionDoc = expressionDocs[i]; @@ -96,13 +114,17 @@ function embed(path, print, textToDoc /*, options */) { doc = docUtils.stripTrailingHardline( textToDoc(text, { parser: "graphql" }) ); - } catch (_error) { + } catch (error) { + if (process.env.PRETTIER_DEBUG) { + throw error; + } // Bail if any part fails to parse. return null; } } if (doc) { + doc = escapeBackticks(doc); if (!isFirst && startsWithBlankLine) { parts.push(""); } @@ -143,21 +165,20 @@ function embed(path, print, textToDoc /*, options */) { (parentParent.tag.name === "md" || parentParent.tag.name === "markdown"))) ) { - const doc = textToDoc( - // leading whitespaces matter in markdown - dedent(parent.quasis[0].value.cooked), - { - parser: "markdown", - __inJsTemplate: true - } - ); + const text = parent.quasis[0].value.cooked; + const indentation = getIndentation(text); + const hasIndent = indentation !== ""; return concat([ - indent( - concat([ - softline, - docUtils.stripTrailingHardline(escapeBackticks(doc)) - ]) - ), + hasIndent + ? indent( + concat([ + softline, + printMarkdown( + text.replace(new RegExp(`^${indentation}`, "gm"), "") + ) + ]) + ) + : concat([literalline, dedentToRoot(printMarkdown(text))]), softline ]); } @@ -165,12 +186,16 @@ function embed(path, print, textToDoc /*, options */) { break; } } + + function printMarkdown(text) { + const doc = textToDoc(text, { parser: "markdown", __inJsTemplate: true }); + return docUtils.stripTrailingHardline(escapeBackticks(doc)); + } } -function dedent(str) { - const firstMatchedIndent = str.match(/\n^( *)/m); - const spaces = firstMatchedIndent === null ? 0 : firstMatchedIndent[1].length; - return str.replace(new RegExp(`^ {${spaces}}`, "gm"), "").trim(); +function getIndentation(str) { + const firstMatchedIndent = str.match(/^([^\S\n]*)\S/m); + return firstMatchedIndent === null ? "" : firstMatchedIndent[1]; } function escapeBackticks(doc) { @@ -228,6 +253,7 @@ function replacePlaceholders(quasisDoc, expressionDocs) { } const expressions = expressionDocs.slice(); + let replaceCounter = 0; const newDoc = docUtils.mapDoc(quasisDoc, doc => { if (!doc || !doc.parts || !doc.parts.length) { return doc; @@ -256,12 +282,16 @@ function replacePlaceholders(quasisDoc, expressionDocs) { if (atPlaceholderIndex > -1) { const placeholder = parts[atPlaceholderIndex]; const rest = parts.slice(atPlaceholderIndex + 1); - + const placeholderMatch = placeholder.match( + /@prettier-placeholder-(.+)-id(.*)/ + ); + const placeholderID = placeholderMatch[1]; // When the expression has a suffix appended, like: // animation: linear ${time}s ease-out; - const suffix = placeholder.slice("@prettier-placeholder".length); + const suffix = placeholderMatch[2]; + const expression = expressions[placeholderID]; - const expression = expressions.shift(); + replaceCounter++; parts = parts .slice(0, atPlaceholderIndex) .concat(["${", expression, "}" + suffix]) @@ -272,7 +302,7 @@ function replacePlaceholders(quasisDoc, expressionDocs) { }); }); - return expressions.length === 0 ? newDoc : null; + return expressions.length === replaceCounter ? newDoc : null; } function printGraphqlComments(lines) { diff --git a/src/language-js/index.js b/src/language-js/index.js index ae20ff44..69d40941 100644 --- a/src/language-js/index.js +++ b/src/language-js/index.js @@ -1,10 +1,68 @@ "use strict"; const printer = require("./printer-estree"); +const hasPragma = require("./pragma").hasPragma; +const options = require("./options"); +const privateUtil = require("../common/util"); // Based on: // https://github.com/github/linguist/blob/master/lib/linguist/languages.yml +const locStart = function(node) { + // Handle nodes with decorators. They should start at the first decorator + if ( + node.declaration && + node.declaration.decorators && + node.declaration.decorators.length > 0 + ) { + return locStart(node.declaration.decorators[0]); + } + if (node.decorators && node.decorators.length > 0) { + return locStart(node.decorators[0]); + } + + if (node.__location) { + return node.__location.startOffset; + } + if (node.range) { + return node.range[0]; + } + if (typeof node.start === "number") { + return node.start; + } + if (node.loc) { + return node.loc.start; + } + return null; +}; + +const locEnd = function(node) { + const endNode = node.nodes && privateUtil.getLast(node.nodes); + if (endNode && node.source && !node.source.end) { + node = endNode; + } + + let loc; + if (node.range) { + loc = node.range[1]; + } else if (typeof node.end === "number") { + loc = node.end; + } + + if (node.__location) { + return node.__location.endOffset; + } + if (node.typeAnnotation) { + return Math.max(loc, locEnd(node.typeAnnotation)); + } + + if (node.loc && !loc) { + return node.loc.end; + } + + return loc; +}; + const languages = [ { name: "JavaScript", @@ -103,24 +161,37 @@ const typescript = { get parse() { return eval("require")("./parser-typescript"); }, - astFormat: "estree" + astFormat: "estree", + hasPragma, + locStart, + locEnd }; const babylon = { get parse() { return eval("require")("./parser-babylon"); }, - astFormat: "estree" + astFormat: "estree", + hasPragma, + locStart, + locEnd }; const parsers = { babylon, - json: babylon, + json: Object.assign({}, babylon, { + hasPragma() { + return false; + } + }), flow: { get parse() { return eval("require")("./parser-flow"); }, - astFormat: "estree" + astFormat: "estree", + hasPragma, + locStart, + locEnd }, "typescript-eslint": typescript, // TODO: Delete this in 2.0 @@ -133,6 +204,9 @@ const printers = { module.exports = { languages, + options, parsers, - printers + printers, + locStart, + locEnd }; diff --git a/src/language-js/needs-parens.js b/src/language-js/needs-parens.js new file mode 100644 index 00000000..3c1b912f --- /dev/null +++ b/src/language-js/needs-parens.js @@ -0,0 +1,560 @@ +"use strict"; + +const assert = require("assert"); + +const util = require("../common/util"); + +function needsParens(path, options) { + const parent = path.getParentNode(); + if (!parent) { + return false; + } + + const name = path.getName(); + const node = path.getNode(); + + // If the value of this path is some child of a Node and not a Node + // itself, then it doesn't need parentheses. Only Node objects (in + // fact, only Expression nodes) need parentheses. + if (path.getValue() !== node) { + return false; + } + + // Only statements don't need parentheses. + if (isStatement(node)) { + return false; + } + + // Closure compiler requires that type casted expressions to be surrounded by + // parentheses. + if ( + util.hasClosureCompilerTypeCastComment( + options.originalText, + node, + options.locEnd + ) + ) { + return true; + } + + // Identifiers never need parentheses. + if (node.type === "Identifier") { + return false; + } + + if (parent.type === "ParenthesizedExpression") { + return false; + } + + // Add parens around the extends clause of a class. It is needed for almost + // all expressions. + if ( + (parent.type === "ClassDeclaration" || parent.type === "ClassExpression") && + parent.superClass === node && + (node.type === "ArrowFunctionExpression" || + node.type === "AssignmentExpression" || + node.type === "AwaitExpression" || + node.type === "BinaryExpression" || + node.type === "ConditionalExpression" || + node.type === "LogicalExpression" || + node.type === "NewExpression" || + node.type === "ObjectExpression" || + node.type === "ParenthesizedExpression" || + node.type === "SequenceExpression" || + node.type === "TaggedTemplateExpression" || + node.type === "UnaryExpression" || + node.type === "UpdateExpression" || + node.type === "YieldExpression") + ) { + return true; + } + + if ( + (parent.type === "ArrowFunctionExpression" && + parent.body === node && + node.type !== "SequenceExpression" && // these have parens added anyway + util.startsWithNoLookaheadToken( + node, + /* forbidFunctionAndClass */ false + )) || + (parent.type === "ExpressionStatement" && + util.startsWithNoLookaheadToken(node, /* forbidFunctionAndClass */ true)) + ) { + return true; + } + + switch (node.type) { + case "CallExpression": { + let firstParentNotMemberExpression = parent; + let i = 0; + while ( + firstParentNotMemberExpression && + firstParentNotMemberExpression.type === "MemberExpression" + ) { + firstParentNotMemberExpression = path.getParentNode(++i); + } + + if ( + firstParentNotMemberExpression.type === "NewExpression" && + firstParentNotMemberExpression.callee === path.getParentNode(i - 1) + ) { + return true; + } + return false; + } + + case "SpreadElement": + case "SpreadProperty": + return ( + parent.type === "MemberExpression" && + name === "object" && + parent.object === node + ); + + case "UpdateExpression": + if (parent.type === "UnaryExpression") { + return ( + node.prefix && + ((node.operator === "++" && parent.operator === "+") || + (node.operator === "--" && parent.operator === "-")) + ); + } + // else fallthrough + case "UnaryExpression": + switch (parent.type) { + case "UnaryExpression": + return ( + node.operator === parent.operator && + (node.operator === "+" || node.operator === "-") + ); + + case "MemberExpression": + return name === "object" && parent.object === node; + + case "TaggedTemplateExpression": + return true; + + case "NewExpression": + case "CallExpression": + return name === "callee" && parent.callee === node; + + case "BinaryExpression": + return parent.operator === "**" && name === "left"; + + case "TSNonNullExpression": + return true; + + default: + return false; + } + + case "BinaryExpression": { + if (parent.type === "UpdateExpression") { + return true; + } + + const isLeftOfAForStatement = node => { + let i = 0; + while (node) { + const parent = path.getParentNode(i++); + if (!parent) { + return false; + } + if (parent.type === "ForStatement" && parent.init === node) { + return true; + } + node = parent; + } + return false; + }; + if (node.operator === "in" && isLeftOfAForStatement(node)) { + return true; + } + } + // fallthrough + case "TSTypeAssertionExpression": + case "TSAsExpression": + case "LogicalExpression": + switch (parent.type) { + case "ConditionalExpression": + return node.type === "TSAsExpression"; + + case "CallExpression": + case "NewExpression": + return name === "callee" && parent.callee === node; + + case "ClassDeclaration": + case "TSAbstractClassDeclaration": + return name === "superClass" && parent.superClass === node; + case "TSTypeAssertionExpression": + case "TaggedTemplateExpression": + case "UnaryExpression": + case "SpreadElement": + case "SpreadProperty": + case "ExperimentalSpreadProperty": + case "BindExpression": + case "AwaitExpression": + case "TSAsExpression": + case "TSNonNullExpression": + case "UpdateExpression": + return true; + + case "MemberExpression": + return name === "object" && parent.object === node; + + case "AssignmentExpression": + return ( + parent.left === node && + (node.type === "TSTypeAssertionExpression" || + node.type === "TSAsExpression") + ); + case "Decorator": + return ( + parent.expression === node && + (node.type === "TSTypeAssertionExpression" || + node.type === "TSAsExpression") + ); + + case "BinaryExpression": + case "LogicalExpression": { + if (!node.operator && node.type !== "TSTypeAssertionExpression") { + return true; + } + + const po = parent.operator; + const pp = util.getPrecedence(po); + const no = node.operator; + const np = util.getPrecedence(no); + + if (pp > np) { + return true; + } + + if ((po === "||" || po === "??") && no === "&&") { + return true; + } + + if (pp === np && name === "right") { + assert.strictEqual(parent.right, node); + return true; + } + + if (pp === np && !util.shouldFlatten(po, no)) { + return true; + } + + // Add parenthesis when working with binary operators + // It's not stricly needed but helps with code understanding + if (util.isBitwiseOperator(po)) { + return true; + } + + return false; + } + + default: + return false; + } + + case "TSParenthesizedType": { + const grandParent = path.getParentNode(1); + if ( + (parent.type === "TSTypeParameter" || + parent.type === "TypeParameter" || + parent.type === "VariableDeclarator" || + parent.type === "TSTypeAnnotation" || + parent.type === "GenericTypeAnnotation" || + parent.type === "TSTypeReference") && + (node.typeAnnotation.type === "TSTypeAnnotation" && + node.typeAnnotation.typeAnnotation.type !== "TSFunctionType" && + grandParent.type !== "TSTypeOperator") + ) { + return false; + } + // Delegate to inner TSParenthesizedType + if (node.typeAnnotation.type === "TSParenthesizedType") { + return false; + } + return true; + } + + case "SequenceExpression": + switch (parent.type) { + case "ReturnStatement": + return false; + + case "ForStatement": + // Although parentheses wouldn't hurt around sequence + // expressions in the head of for loops, traditional style + // dictates that e.g. i++, j++ should not be wrapped with + // parentheses. + return false; + + case "ExpressionStatement": + return name !== "expression"; + + case "ArrowFunctionExpression": + // We do need parentheses, but SequenceExpressions are handled + // specially when printing bodies of arrow functions. + return name !== "body"; + + default: + // Otherwise err on the side of overparenthesization, adding + // explicit exceptions above if this proves overzealous. + return true; + } + + case "YieldExpression": + if ( + parent.type === "UnaryExpression" || + parent.type === "AwaitExpression" || + parent.type === "TSAsExpression" || + parent.type === "TSNonNullExpression" + ) { + return true; + } + // else fallthrough + case "AwaitExpression": + switch (parent.type) { + case "TaggedTemplateExpression": + case "BinaryExpression": + case "LogicalExpression": + case "SpreadElement": + case "SpreadProperty": + case "ExperimentalSpreadProperty": + case "TSAsExpression": + case "TSNonNullExpression": + return true; + + case "MemberExpression": + return parent.object === node; + + case "NewExpression": + case "CallExpression": + return parent.callee === node; + + case "ConditionalExpression": + return parent.test === node; + + default: + return false; + } + + case "ArrayTypeAnnotation": + return parent.type === "NullableTypeAnnotation"; + + case "IntersectionTypeAnnotation": + case "UnionTypeAnnotation": + return ( + parent.type === "ArrayTypeAnnotation" || + parent.type === "NullableTypeAnnotation" || + parent.type === "IntersectionTypeAnnotation" || + parent.type === "UnionTypeAnnotation" + ); + + case "NullableTypeAnnotation": + return parent.type === "ArrayTypeAnnotation"; + + case "FunctionTypeAnnotation": + return ( + parent.type === "UnionTypeAnnotation" || + parent.type === "IntersectionTypeAnnotation" || + parent.type === "ArrayTypeAnnotation" + ); + + case "StringLiteral": + case "NumericLiteral": + case "Literal": + if ( + typeof node.value === "string" && + parent.type === "ExpressionStatement" && + // TypeScript workaround for eslint/typescript-eslint-parser#267 + // See corresponding workaround in printer.js case: "Literal" + ((options.parser !== "typescript" && !parent.directive) || + (options.parser === "typescript" && + options.originalText.substr(options.locStart(node) - 1, 1) === "(")) + ) { + // To avoid becoming a directive + const grandParent = path.getParentNode(1); + + return ( + grandParent.type === "Program" || + grandParent.type === "BlockStatement" + ); + } + + return ( + parent.type === "MemberExpression" && + typeof node.value === "number" && + name === "object" && + parent.object === node + ); + + case "AssignmentExpression": { + const grandParent = path.getParentNode(1); + + if (parent.type === "ArrowFunctionExpression" && parent.body === node) { + return true; + } else if ( + parent.type === "ClassProperty" && + parent.key === node && + parent.computed + ) { + return false; + } else if ( + parent.type === "TSPropertySignature" && + parent.name === node + ) { + return false; + } else if ( + parent.type === "ForStatement" && + (parent.init === node || parent.update === node) + ) { + return false; + } else if (parent.type === "ExpressionStatement") { + return node.left.type === "ObjectPattern"; + } else if (parent.type === "TSPropertySignature" && parent.key === node) { + return false; + } else if (parent.type === "AssignmentExpression") { + return false; + } else if ( + parent.type === "SequenceExpression" && + grandParent && + grandParent.type === "ForStatement" && + (grandParent.init === parent || grandParent.update === parent) + ) { + return false; + } + return true; + } + case "ConditionalExpression": + switch (parent.type) { + case "TaggedTemplateExpression": + case "UnaryExpression": + case "SpreadElement": + case "SpreadProperty": + case "ExperimentalSpreadProperty": + case "BinaryExpression": + case "LogicalExpression": + case "ExportDefaultDeclaration": + case "AwaitExpression": + case "JSXSpreadAttribute": + case "TSTypeAssertionExpression": + case "TypeCastExpression": + case "TSAsExpression": + case "TSNonNullExpression": + return true; + + case "NewExpression": + case "CallExpression": + return name === "callee" && parent.callee === node; + + case "ConditionalExpression": + return name === "test" && parent.test === node; + + case "MemberExpression": + return name === "object" && parent.object === node; + + default: + return false; + } + + case "FunctionExpression": + switch (parent.type) { + case "CallExpression": + return name === "callee"; // Not strictly necessary, but it's clearer to the reader if IIFEs are wrapped in parentheses. + case "TaggedTemplateExpression": + return true; // This is basically a kind of IIFE. + case "ExportDefaultDeclaration": + return true; + default: + return false; + } + + case "ArrowFunctionExpression": + switch (parent.type) { + case "CallExpression": + return name === "callee"; + + case "NewExpression": + return name === "callee"; + + case "MemberExpression": + return name === "object"; + + case "TSAsExpression": + case "BindExpression": + case "TaggedTemplateExpression": + case "UnaryExpression": + case "LogicalExpression": + case "BinaryExpression": + case "AwaitExpression": + case "TSTypeAssertionExpression": + return true; + + case "ConditionalExpression": + return name === "test"; + + default: + return false; + } + + case "ClassExpression": + return parent.type === "ExportDefaultDeclaration"; + } + + return false; +} + +function isStatement(node) { + return ( + node.type === "BlockStatement" || + node.type === "BreakStatement" || + node.type === "ClassBody" || + node.type === "ClassDeclaration" || + node.type === "ClassMethod" || + node.type === "ClassProperty" || + node.type === "ClassPrivateProperty" || + node.type === "ContinueStatement" || + node.type === "DebuggerStatement" || + node.type === "DeclareClass" || + node.type === "DeclareExportAllDeclaration" || + node.type === "DeclareExportDeclaration" || + node.type === "DeclareFunction" || + node.type === "DeclareInterface" || + node.type === "DeclareModule" || + node.type === "DeclareModuleExports" || + node.type === "DeclareVariable" || + node.type === "DoWhileStatement" || + node.type === "ExportAllDeclaration" || + node.type === "ExportDefaultDeclaration" || + node.type === "ExportNamedDeclaration" || + node.type === "ExpressionStatement" || + node.type === "ForAwaitStatement" || + node.type === "ForInStatement" || + node.type === "ForOfStatement" || + node.type === "ForStatement" || + node.type === "FunctionDeclaration" || + node.type === "IfStatement" || + node.type === "ImportDeclaration" || + node.type === "InterfaceDeclaration" || + node.type === "LabeledStatement" || + node.type === "MethodDefinition" || + node.type === "ReturnStatement" || + node.type === "SwitchStatement" || + node.type === "ThrowStatement" || + node.type === "TryStatement" || + node.type === "TSAbstractClassDeclaration" || + node.type === "TSEnumDeclaration" || + node.type === "TSImportEqualsDeclaration" || + node.type === "TSInterfaceDeclaration" || + node.type === "TSModuleDeclaration" || + node.type === "TSNamespaceExportDeclaration" || + node.type === "TypeAlias" || + node.type === "VariableDeclaration" || + node.type === "WhileStatement" || + node.type === "WithStatement" + ); +} + +module.exports = needsParens; diff --git a/src/language-js/pragma.js b/src/language-js/pragma.js new file mode 100644 index 00000000..031359a8 --- /dev/null +++ b/src/language-js/pragma.js @@ -0,0 +1,25 @@ +"use strict"; + +const docblock = require("jest-docblock"); + +function hasPragma(text) { + const pragmas = Object.keys(docblock.parse(docblock.extract(text))); + return pragmas.indexOf("prettier") !== -1 || pragmas.indexOf("format") !== -1; +} + +function insertPragma(text) { + const parsedDocblock = docblock.parseWithComments(docblock.extract(text)); + const pragmas = Object.assign({ format: "" }, parsedDocblock.pragmas); + const newDocblock = docblock.print({ + pragmas, + comments: parsedDocblock.comments.replace(/^(\s+?\r?\n)+/, "") // remove leading newlines + }); + const strippedText = docblock.strip(text); + const separatingNewlines = strippedText.startsWith("\n") ? "\n" : "\n\n"; + return newDocblock + separatingNewlines + strippedText; +} + +module.exports = { + hasPragma, + insertPragma +}; diff --git a/src/language-js/printer-estree.js b/src/language-js/printer-estree.js index 09fbf017..87372b7d 100644 --- a/src/language-js/printer-estree.js +++ b/src/language-js/printer-estree.js @@ -3,11 +3,14 @@ const assert = require("assert"); // TODO(azz): anything that imports from main shouldn't be in a `language-*` dir. const comments = require("../main/comments"); -const util = require("../common/util"); +const privateUtil = require("../common/util"); +const sharedUtil = require("../common/util-shared"); const isIdentifierName = require("esutils").keyword.isIdentifierNameES6; const embed = require("./embed"); -const printerOptions = require("./options"); const clean = require("./clean"); +const insertPragma = require("./pragma").insertPragma; +const handleComments = require("./comments"); +const pathNeedsParens = require("./needs-parens"); const doc = require("../doc"); const docBuilders = doc.builders; @@ -26,6 +29,7 @@ const ifBreak = docBuilders.ifBreak; const breakParent = docBuilders.breakParent; const lineSuffixBoundary = docBuilders.lineSuffixBoundary; const addAlignmentToDoc = docBuilders.addAlignmentToDoc; +const dedent = docBuilders.dedent; const docUtils = doc.utils; const willBreak = docUtils.willBreak; @@ -68,7 +72,7 @@ function genericPrint(path, options, printPath, args) { node.decorators.length > 0 && // If the parent node is an export declaration, it will be // responsible for printing node.decorators. - !util.getParentExportDeclaration(path) + !privateUtil.getParentExportDeclaration(path) ) { let separator = hardline; path.each(decoratorPath => { @@ -99,7 +103,7 @@ function genericPrint(path, options, printPath, args) { decorators.push(prefix, printPath(decoratorPath), separator); }, "decorators"); } else if ( - util.isExportDeclaration(node) && + privateUtil.isExportDeclaration(node) && node.declaration && node.declaration.decorators ) { @@ -116,8 +120,8 @@ function genericPrint(path, options, printPath, args) { ); } else { // Nodes with decorators can't have parentheses, so we can avoid - // computing path.needsParens() except in this case. - needsParens = path.needsParens(options); + // computing pathNeedsParens() except in this case. + needsParens = pathNeedsParens(path, options); } const parts = []; @@ -138,7 +142,7 @@ function genericPrint(path, options, printPath, args) { } function hasPrettierIgnore(path) { - return util.hasIgnoreComment(path) || hasJsxIgnoreComment(path); + return privateUtil.hasIgnoreComment(path) || hasJsxIgnoreComment(path); } function hasJsxIgnoreComment(path) { @@ -171,6 +175,145 @@ function hasJsxIgnoreComment(path) { ); } +// The following is the shared logic for +// ternary operators, namely ConditionalExpression +// and TSConditionalType +function formatTernaryOperator(path, options, print, operatorOptions) { + const n = path.getValue(); + const parts = []; + const operatorOpts = Object.assign( + { + beforeParts: () => [""], + afterParts: () => [""], + shouldCheckJsx: true, + operatorName: "ConditionalExpression", + consequentNode: "consequent", + alternateNode: "alternate", + testNode: "test", + breakNested: true + }, + operatorOptions || {} + ); + + // We print a ConditionalExpression in either "JSX mode" or "normal mode". + // See tests/jsx/conditional-expression.js for more info. + let jsxMode = false; + const parent = path.getParentNode(); + let forceNoIndent = parent.type === operatorOpts.operatorName; + + // Find the outermost non-ConditionalExpression parent, and the outermost + // ConditionalExpression parent. We'll use these to determine if we should + // print in JSX mode. + let currentParent; + let previousParent; + let i = 0; + do { + previousParent = currentParent || n; + currentParent = path.getParentNode(i); + i++; + } while (currentParent && currentParent.type === operatorOpts.operatorName); + const firstNonConditionalParent = currentParent || parent; + const lastConditionalParent = previousParent; + + if ( + (operatorOpts.shouldCheckJsx && isJSXNode(n[operatorOpts.testNode])) || + isJSXNode(n[operatorOpts.consequentNode]) || + isJSXNode(n[operatorOpts.alternateNode]) || + conditionalExpressionChainContainsJSX(lastConditionalParent) + ) { + jsxMode = true; + forceNoIndent = true; + + // Even though they don't need parens, we wrap (almost) everything in + // parens when using ?: within JSX, because the parens are analogous to + // curly braces in an if statement. + const wrap = doc => + concat([ + ifBreak("(", ""), + indent(concat([softline, doc])), + softline, + ifBreak(")", "") + ]); + + // The only things we don't wrap are: + // * Nested conditional expressions in alternates + // * null + const isNull = node => + node.type === "NullLiteral" || + (node.type === "Literal" && node.value === null); + + parts.push( + " ? ", + isNull(n[operatorOpts.consequentNode]) + ? path.call(print, operatorOpts.consequentNode) + : wrap(path.call(print, operatorOpts.consequentNode)), + " : ", + n[operatorOpts.alternateNode].type === operatorOpts.operatorName || + isNull(n[operatorOpts.alternateNode]) + ? path.call(print, operatorOpts.alternateNode) + : wrap(path.call(print, operatorOpts.alternateNode)) + ); + } else { + // normal mode + const part = concat([ + line, + "? ", + n[operatorOpts.consequentNode].type === operatorOpts.operatorName + ? ifBreak("", "(") + : "", + align(2, path.call(print, operatorOpts.consequentNode)), + n[operatorOpts.consequentNode].type === operatorOpts.operatorName + ? ifBreak("", ")") + : "", + line, + ": ", + align(2, path.call(print, operatorOpts.alternateNode)) + ]); + parts.push( + parent.type === operatorOpts.operatorName + ? options.useTabs + ? dedent(indent(part)) + : align(Math.max(0, options.tabWidth - 2), part) + : part + ); + } + + // We want a whole chain of ConditionalExpressions to all + // break if any of them break. That means we should only group around the + // outer-most ConditionalExpression. + const maybeGroup = doc => + operatorOpts.breakNested + ? parent === firstNonConditionalParent ? group(doc) : doc + : group(doc); // Always group in normal mode. + + // Break the closing paren to keep the chain right after it: + // (a + // ? b + // : c + // ).call() + const breakClosingParen = + !jsxMode && parent.type === "MemberExpression" && !parent.computed; + + return maybeGroup( + concat( + [].concat( + operatorOpts.beforeParts(), + forceNoIndent ? concat(parts) : indent(concat(parts)), + operatorOpts.afterParts(breakClosingParen) + ) + ) + ); +} + +function getTypeScriptMappedTypeModifier(tokenNode, keyword) { + if (tokenNode.type === "TSPlusToken") { + return "+" + keyword; + } else if (tokenNode.type === "TSMinusToken") { + return "-" + keyword; + } + return keyword; +} + function printPathNoParens(path, options, print, args) { const n = path.getValue(); const semi = options.semi ? ";" : ""; @@ -193,7 +336,11 @@ function printPathNoParens(path, options, print, args) { path.each(childPath => { parts.push(print(childPath), semi, hardline); if ( - util.isNextLineEmpty(options.originalText, childPath.getValue()) + sharedUtil.isNextLineEmpty( + options.originalText, + childPath.getValue(), + options + ) ) { parts.push(hardline); } @@ -302,11 +449,15 @@ function printPathNoParens(path, options, print, args) { const shouldIndentIfInlining = parent.type === "AssignmentExpression" || parent.type === "VariableDeclarator" || + parent.type === "ClassProperty" || + parent.type === "TSAbstractClassProperty" || + parent.type === "ClassPrivateProperty" || parent.type === "ObjectProperty" || parent.type === "Property"; const samePrecedenceSubExpression = - isBinaryish(n.left) && util.shouldFlatten(n.operator, n.left.operator); + isBinaryish(n.left) && + privateUtil.shouldFlatten(n.operator, n.left.operator); if ( shouldNotIndent || @@ -452,9 +603,10 @@ function printPathNoParens(path, options, print, args) { options, /* sameIndent */ true, comment => { - const nextCharacter = util.getNextNonSpaceNonCommentCharacterIndex( + const nextCharacter = sharedUtil.getNextNonSpaceNonCommentCharacterIndex( options.originalText, - comment + comment, + options ); return options.originalText.substr(nextCharacter, 2) === "=>"; } @@ -470,12 +622,12 @@ function printPathNoParens(path, options, print, args) { // We want to always keep these types of nodes on the same line // as the arrow. if ( - !hasLeadingOwnLineComment(options.originalText, n.body) && + !hasLeadingOwnLineComment(options.originalText, n.body, options) && (n.body.type === "ArrayExpression" || n.body.type === "ObjectExpression" || n.body.type === "BlockStatement" || isJSXNode(n.body) || - isTemplateOnItsOwnLine(n.body, options.originalText) || + isTemplateOnItsOwnLine(n.body, options.originalText, options) || n.body.type === "ArrowFunctionExpression") ) { return group(concat([concat(parts), " ", body])); @@ -499,8 +651,9 @@ function printPathNoParens(path, options, print, args) { // with the opening (, or if it's inside a JSXExpression (e.g. an attribute) // we should align the expression's closing } with the line with the opening {. const shouldAddSoftLine = - (args && args.expandLastArg) || - path.getParentNode().type === "JSXExpressionContainer"; + ((args && args.expandLastArg) || + path.getParentNode().type === "JSXExpressionContainer") && + !(n.comments && n.comments.length); const printTrailingComma = args && args.expandLastArg && shouldPrintComma(options, "all"); @@ -510,7 +663,7 @@ function printPathNoParens(path, options, print, args) { // a <= a ? a : a const shouldAddParens = n.body.type === "ConditionalExpression" && - !util.startsWithNoLookaheadToken( + !privateUtil.startsWithNoLookaheadToken( n.body, /* forbidFunctionAndClass */ false ); @@ -688,7 +841,10 @@ function printPathNoParens(path, options, print, args) { (n.importKind && n.importKind === "type") || // import {} from 'x' /{\s*}/.test( - options.originalText.slice(util.locStart(n), util.locStart(n.source)) + options.originalText.slice( + options.locStart(n), + options.locStart(n.source) + ) ) ) { parts.push("{} from "); @@ -735,7 +891,11 @@ function printPathNoParens(path, options, print, args) { path.each(childPath => { parts.push(indent(concat([hardline, print(childPath), semi]))); if ( - util.isNextLineEmpty(options.originalText, childPath.getValue()) + sharedUtil.isNextLineEmpty( + options.originalText, + childPath.getValue(), + options + ) ) { parts.push(hardline); } @@ -759,8 +919,8 @@ function printPathNoParens(path, options, print, args) { parts.push( concat([ " (", - indent(concat([softline, path.call(print, "argument")])), - line, + indent(concat([hardline, path.call(print, "argument")])), + hardline, ")" ]) ); @@ -800,17 +960,23 @@ function printPathNoParens(path, options, print, args) { const optional = printOptionalToken(path); if ( - // We want to keep require calls as a unit + // We want to keep CommonJS- and AMD-style require calls, and AMD-style + // define calls, as a unit. + // e.g. `define(["some/lib", (lib) => {` (!isNew && n.callee.type === "Identifier" && - n.callee.name === "require") || + (n.callee.name === "require" || n.callee.name === "define")) || n.callee.type === "Import" || // Template literals as single arguments (n.arguments.length === 1 && - isTemplateOnItsOwnLine(n.arguments[0], options.originalText)) || + isTemplateOnItsOwnLine( + n.arguments[0], + options.originalText, + options + )) || // Keep test declarations on a single line // e.g. `it('long name', () => {` - (!isNew && isTestCall(n)) + (!isNew && isTestCall(n, path.getParentNode())) ) { return concat([ isNew ? "new " : "", @@ -873,15 +1039,26 @@ function printPathNoParens(path, options, print, args) { case "TSInterfaceBody": case "TSTypeLiteral": { const isTypeAnnotation = n.type === "ObjectTypeAnnotation"; + const parent = path.getParentNode(0); const shouldBreak = n.type === "TSInterfaceBody" || + (n.type === "ObjectPattern" && + parent.type !== "FunctionDeclaration" && + parent.type !== "FunctionExpression" && + parent.type !== "ArrowFunctionExpression" && + parent.type !== "AssignmentPattern" && + n.properties.some( + property => + property.value && + (property.value.type === "ObjectPattern" || + property.value.type === "ArrayPattern") + )) || (n.type !== "ObjectPattern" && - util.hasNewlineInRange( + privateUtil.hasNewlineInRange( options.originalText, - util.locStart(n), - util.locEnd(n) + options.locStart(n), + options.locEnd(n) )); - const parent = path.getParentNode(0); const isFlowInterfaceLikeBody = isTypeAnnotation && parent && @@ -923,7 +1100,7 @@ function printPathNoParens(path, options, print, args) { propsAndLoc.push({ node: node, printed: print(childPath), - loc: util.locStart(node) + loc: options.locStart(node) }); }, field); }); @@ -934,24 +1111,26 @@ function printPathNoParens(path, options, print, args) { separatorParts = [separator, line]; if ( prop.node.type === "TSPropertySignature" && - util.hasNodeIgnoreComment(prop.node) + privateUtil.hasNodeIgnoreComment(prop.node) ) { separatorParts.shift(); } - if (util.isNextLineEmpty(options.originalText, prop.node)) { + if ( + sharedUtil.isNextLineEmpty(options.originalText, prop.node, options) + ) { separatorParts.push(hardline); } return result; }); - const lastElem = util.getLast(n[propertiesField]); + const lastElem = privateUtil.getLast(n[propertiesField]); const canHaveTrailingSeparator = !( lastElem && (lastElem.type === "RestProperty" || lastElem.type === "RestElement" || lastElem.type === "ExperimentalRestProperty" || - util.hasNodeIgnoreComment(lastElem)) + privateUtil.hasNodeIgnoreComment(lastElem)) ); let content; @@ -999,6 +1178,7 @@ function printPathNoParens(path, options, print, args) { (shouldHugType(n) && parentParentParent && shouldHugArguments(parentParentParent) && + parentParentParent.params[0].typeAnnotation && parentParentParent.params[0].typeAnnotation.typeAnnotation === n) ) { return content; @@ -1065,7 +1245,7 @@ function printPathNoParens(path, options, print, args) { ); } } else { - const lastElem = util.getLast(n.elements); + const lastElem = privateUtil.getLast(n.elements); const canHaveTrailingComma = !( lastElem && lastElem.type === "RestElement" ); @@ -1078,7 +1258,7 @@ function printPathNoParens(path, options, print, args) { // [1,].length === 1 // [1,,].length === 2 // - // Note that util.getLast returns null if the array is empty, but + // Note that privateUtil.getLast returns null if the array is empty, but // we already check for an empty array just above so we are safe const needsForcedTrailingComma = canHaveTrailingComma && lastElem === null; @@ -1151,7 +1331,7 @@ function printPathNoParens(path, options, print, args) { case "RegExpLiteral": // Babel 6 Literal split return printRegex(n); case "NumericLiteral": // Babel 6 Literal split - return util.printNumber(n.extra.raw); + return privateUtil.printNumber(n.extra.raw); case "BooleanLiteral": // Babel 6 Literal split case "StringLiteral": // Babel 6 Literal split case "Literal": { @@ -1159,13 +1339,13 @@ function printPathNoParens(path, options, print, args) { return printRegex(n.regex); } if (typeof n.value === "number") { - return util.printNumber(n.raw); + return privateUtil.printNumber(n.raw); } if (typeof n.value !== "string") { return "" + n.value; } // TypeScript workaround for eslint/typescript-eslint-parser#267 - // See corresponding workaround in fast-path.js needsParens() + // See corresponding workaround in needs-parens.js const grandParent = path.getParentNode(1); const isTypeScriptDirective = options.parser === "typescript" && @@ -1198,102 +1378,11 @@ function printPathNoParens(path, options, print, args) { } return concat(parts); - case "ConditionalExpression": { - // We print a ConditionalExpression in either "JSX mode" or "normal mode". - // See tests/jsx/conditional-expression.js for more info. - let jsxMode = false; - const parent = path.getParentNode(); - let forceNoIndent = parent.type === "ConditionalExpression"; - - // Find the outermost non-ConditionalExpression parent, and the outermost - // ConditionalExpression parent. We'll use these to determine if we should - // print in JSX mode. - let currentParent; - let previousParent; - let i = 0; - do { - previousParent = currentParent || n; - currentParent = path.getParentNode(i); - i++; - } while (currentParent && currentParent.type === "ConditionalExpression"); - const firstNonConditionalParent = currentParent || parent; - const lastConditionalParent = previousParent; - - if ( - isJSXNode(n.test) || - isJSXNode(n.consequent) || - isJSXNode(n.alternate) || - conditionalExpressionChainContainsJSX(lastConditionalParent) - ) { - jsxMode = true; - forceNoIndent = true; - - // Even though they don't need parens, we wrap (almost) everything in - // parens when using ?: within JSX, because the parens are analogous to - // curly braces in an if statement. - const wrap = doc => - concat([ - ifBreak("(", ""), - indent(concat([softline, doc])), - softline, - ifBreak(")", "") - ]); - - // The only things we don't wrap are: - // * Nested conditional expressions in alternates - // * null - const isNull = node => - node.type === "NullLiteral" || - (node.type === "Literal" && node.value === null); - - parts.push( - " ? ", - isNull(n.consequent) - ? path.call(print, "consequent") - : wrap(path.call(print, "consequent")), - " : ", - n.alternate.type === "ConditionalExpression" || isNull(n.alternate) - ? path.call(print, "alternate") - : wrap(path.call(print, "alternate")) - ); - } else { - // normal mode - parts.push( - line, - "? ", - n.consequent.type === "ConditionalExpression" ? ifBreak("", "(") : "", - align(2, path.call(print, "consequent")), - n.consequent.type === "ConditionalExpression" ? ifBreak("", ")") : "", - line, - ": ", - align(2, path.call(print, "alternate")) - ); - } - - // In JSX mode, we want a whole chain of ConditionalExpressions to all - // break if any of them break. That means we should only group around the - // outer-most ConditionalExpression. - const maybeGroup = doc => - jsxMode - ? parent === firstNonConditionalParent ? group(doc) : doc - : group(doc); // Always group in normal mode. - - // Break the closing paren to keep the chain right after it: - // (a - // ? b - // : c - // ).call() - const breakClosingParen = - !jsxMode && parent.type === "MemberExpression" && !parent.computed; - - return maybeGroup( - concat([ - path.call(print, "test"), - forceNoIndent ? concat(parts) : indent(concat(parts)), - breakClosingParen ? softline : "" - ]) - ); - } + case "ConditionalExpression": + return formatTernaryOperator(path, options, print, { + beforeParts: () => [path.call(print, "test")], + afterParts: breakClosingParen => [breakClosingParen ? softline : ""] + }); case "VariableDeclaration": { const printed = path.map(childPath => { return print(childPath); @@ -1377,13 +1466,26 @@ function printPathNoParens(path, options, print, args) { parts.push(opening); if (n.alternate) { - if (n.consequent.type === "BlockStatement") { - parts.push(" else"); - } else { - parts.push(hardline, "else"); + const commentOnOwnLine = + (hasTrailingComment(n.consequent) && + n.consequent.comments.some( + comment => + comment.trailing && !privateUtil.isBlockComment(comment) + )) || + needsHardlineAfterDanglingComment(n); + const elseOnSameLine = + n.consequent.type === "BlockStatement" && !commentOnOwnLine; + parts.push(elseOnSameLine ? " " : hardline); + + if (hasDanglingComments(n)) { + parts.push( + comments.printDanglingComments(path, options, true), + commentOnOwnLine ? hardline : " " + ); } parts.push( + "else", group( adjustClause( n.alternate, @@ -1564,9 +1666,15 @@ function printPathNoParens(path, options, print, args) { // Note: ignoring n.lexical because it has no printing consequences. case "SwitchStatement": return concat([ - "switch (", - path.call(print, "discriminant"), - ") {", + group( + concat([ + "switch (", + indent(concat([softline, path.call(print, "discriminant")])), + softline, + ")" + ]) + ), + " {", n.cases.length > 0 ? indent( concat([ @@ -1578,7 +1686,11 @@ function printPathNoParens(path, options, print, args) { return concat([ casePath.call(print), n.cases.indexOf(caseNode) !== n.cases.length - 1 && - util.isNextLineEmpty(options.originalText, caseNode) + sharedUtil.isNextLineEmpty( + options.originalText, + caseNode, + options + ) ? hardline : "" ]); @@ -1741,6 +1853,7 @@ function printPathNoParens(path, options, print, args) { n.attributes.length === 1 && n.attributes[0].value && isStringLiteral(n.attributes[0].value) && + !n.attributes[0].value.value.includes("\n") && // We should break for the following cases: //
+ attr.value && + isStringLiteral(attr.value) && + attr.value.value.includes("\n") + ); + return group( concat([ "<", @@ -1792,7 +1917,8 @@ function printPathNoParens(path, options, print, args) { n.selfClosing ? line : bracketSameLine ? ">" : softline ]), n.selfClosing ? "/>" : bracketSameLine ? "" : ">" - ]) + ]), + { shouldBreak } ); } case "JSXClosingElement": @@ -1803,7 +1929,7 @@ function printPathNoParens(path, options, print, args) { case "TSJsxClosingFragment": { const hasComment = n.comments && n.comments.length; const hasOwnLineComment = - hasComment && !n.comments.every(util.isBlockComment); + hasComment && !n.comments.every(privateUtil.isBlockComment); const isOpeningFragment = n.type === "JSXOpeningFragment" || n.type === "TSJsxOpeningFragment"; return concat([ @@ -1825,7 +1951,7 @@ function printPathNoParens(path, options, print, args) { throw new Error("JSXTest should be handled by JSXElement"); case "JSXEmptyExpression": { const requiresHardline = - n.comments && !n.comments.every(util.isBlockComment); + n.comments && !n.comments.every(privateUtil.isBlockComment); return concat([ comments.printDanglingComments( @@ -1938,7 +2064,7 @@ function printPathNoParens(path, options, print, args) { // expression inside at the beginning of ${ instead of the beginning // of the `. const tabWidth = options.tabWidth; - const indentSize = util.getIndentSize( + const indentSize = privateUtil.getIndentSize( childPath.getValue().value.raw, tabWidth ); @@ -2108,7 +2234,7 @@ function printPathNoParens(path, options, print, args) { (parent.type === "ObjectTypeProperty" && !getFlowVariance(parent) && !parent.optional && - util.locStart(parent) === util.locStart(n)) || + options.locStart(parent) === options.locStart(n)) || parent.type === "ObjectTypeCallProperty" || (parentParentParent && parentParentParent.type === "DeclareFunction") ); @@ -2128,7 +2254,7 @@ function printPathNoParens(path, options, print, args) { parent.type === "TSTypeAnnotation") && parentParent.type === "ArrowFunctionExpression"; - if (isObjectTypePropertyAFunction(parent)) { + if (isObjectTypePropertyAFunction(parent, options)) { isArrowFunctionTypeAnnotation = true; needsColon = true; } @@ -2259,7 +2385,7 @@ function printPathNoParens(path, options, print, args) { !( (parent.type === "TypeAlias" || parent.type === "VariableDeclarator") && - hasLeadingOwnLineComment(options.originalText, n) + hasLeadingOwnLineComment(options.originalText, n, options) ); // { @@ -2302,7 +2428,7 @@ function printPathNoParens(path, options, print, args) { (greatGreatGrandParent.type === "TSUnionType" || greatGreatGrandParent.type === "TSIntersectionType"); } else { - hasParens = path.needsParens(options); + hasParens = pathNeedsParens(path, options); } if (hasParens) { @@ -2349,7 +2475,7 @@ function printPathNoParens(path, options, print, args) { variance || "", printPropertyKey(path, options, print), printOptionalToken(path), - isFunctionNotation(n) ? "" : ": ", + isFunctionNotation(n, options) ? "" : ": ", path.call(print, "value") ]); } @@ -2365,9 +2491,9 @@ function printPathNoParens(path, options, print, args) { assert.strictEqual(typeof n.value, "number"); if (n.extra != null) { - return util.printNumber(n.extra.raw); + return privateUtil.printNumber(n.extra.raw); } - return util.printNumber(n.raw); + return privateUtil.printNumber(n.raw); case "StringTypeAnnotation": return "string"; @@ -2629,7 +2755,7 @@ function printPathNoParens(path, options, print, args) { return concat(parts); } case "TSTypeOperator": - return concat(["keyof ", path.call(print, "typeAnnotation")]); + return concat([n.operator, " ", path.call(print, "typeAnnotation")]); case "TSMappedType": return group( concat([ @@ -2638,13 +2764,21 @@ function printPathNoParens(path, options, print, args) { concat([ options.bracketSpacing ? line : softline, n.readonlyToken - ? concat([path.call(print, "readonlyToken"), " "]) + ? concat([ + getTypeScriptMappedTypeModifier( + n.readonlyToken, + "readonly" + ), + " " + ]) : "", printTypeScriptModifiers(path, options, print), "[", path.call(print, "typeParameter"), "]", - n.questionToken ? "?" : "", + n.questionToken + ? getTypeScriptMappedTypeModifier(n.questionToken, "?") + : "", ": ", path.call(print, "typeAnnotation") ]) @@ -2777,7 +2911,10 @@ function printPathNoParens(path, options, print, args) { n.id.type === "Identifier" && n.id.name === "global" && !/namespace|module/.test( - options.originalText.slice(util.locStart(n), util.locStart(n.id)) + options.originalText.slice( + options.locStart(n), + options.locStart(n.id) + ) ); if (!isGlobalDeclaration) { @@ -2820,6 +2957,26 @@ function printPathNoParens(path, options, print, args) { case "PrivateName": return concat(["#", path.call(print, "id")]); + case "TSConditionalType": + return formatTernaryOperator(path, options, print, { + beforeParts: () => [ + path.call(print, "checkType"), + " ", + "extends", + " ", + path.call(print, "extendsType") + ], + shouldCheckJsx: false, + operatorName: "TSConditionalType", + consequentNode: "trueType", + alternateNode: "falseType", + testNode: "checkType", + breakNested: false + }); + + case "TSInferType": + return concat(["infer", " ", path.call(print, "typeParameter")]); + default: /* istanbul ignore next */ throw new Error("unknown type: " + JSON.stringify(n.type)); @@ -2880,7 +3037,10 @@ function printStatementSequence(path, options, print) { } } - if (util.isNextLineEmpty(text, stmt) && !isLastStatement(stmtPath)) { + if ( + sharedUtil.isNextLineEmpty(text, stmt, options) && + !isLastStatement(stmtPath) + ) { parts.push(hardline); } @@ -2986,8 +3146,8 @@ function couldGroupArg(arg) { } function shouldGroupLastArg(args) { - const lastArg = util.getLast(args); - const penultimateArg = util.getPenultimate(args); + const lastArg = privateUtil.getLast(args); + const penultimateArg = privateUtil.getPenultimate(args); return ( !hasLeadingComment(lastArg) && !hasTrailingComment(lastArg) && @@ -3034,7 +3194,7 @@ function printArgumentsList(path, options, print) { if (index === lastArgIndex) { // do nothing - } else if (util.isNextLineEmpty(options.originalText, arg)) { + } else if (sharedUtil.isNextLineEmpty(options.originalText, arg, options)) { if (index === 0) { hasEmptyLineFollowingFirstArg = true; } @@ -3048,9 +3208,6 @@ function printArgumentsList(path, options, print) { return concat(parts); }, "arguments"); - // This is just an optimization; I think we could return the - // conditional group for all function calls, but it's more expensive - // so only do it for specific forms. const shouldGroupFirst = shouldGroupFirstArg(args); const shouldGroupLast = shouldGroupLastArg(args); if (shouldGroupFirst || shouldGroupLast) { @@ -3109,7 +3266,7 @@ function printArgumentsList(path, options, print) { : concat([ "(", concat(printedArguments.slice(0, -1)), - group(util.getLast(printedExpanded), { + group(privateUtil.getLast(printedExpanded), { shouldBreak: true }), ")" @@ -3149,15 +3306,23 @@ function printTypeAnnotation(path, options, print) { } const parentNode = path.getParentNode(); + const isDefinite = + node.definite || + (parentNode && + parentNode.type === "VariableDeclarator" && + parentNode.definite); + const isFunctionDeclarationIdentifier = parentNode.type === "DeclareFunction" && parentNode.id === node; - if (isFlowAnnotationComment(options.originalText, node.typeAnnotation)) { + if ( + isFlowAnnotationComment(options.originalText, node.typeAnnotation, options) + ) { return concat([" /*: ", path.call(print, "typeAnnotation"), " */"]); } return concat([ - isFunctionDeclarationIdentifier ? "" : ": ", + isFunctionDeclarationIdentifier ? "" : isDefinite ? "!: " : ": ", path.call(print, "typeAnnotation") ]); } @@ -3196,16 +3361,17 @@ function printFunctionParams(path, print, options, expandArg, printTypeParams) { options, /* sameIndent */ true, comment => - util.getNextNonSpaceNonCommentCharacter( + privateUtil.getNextNonSpaceNonCommentCharacter( options.originalText, - comment + comment, + options.locEnd ) === ")" ), ")" ]); } - const lastParam = util.getLast(fun[paramsField]); + const lastParam = privateUtil.getLast(fun[paramsField]); // If the parent is a call with the first/last argument expansion and this is the // params of the first/last argument, we dont want the arguments to break and instead @@ -3264,8 +3430,8 @@ function printFunctionParams(path, print, options, expandArg, printTypeParams) { ]; const isFlowShorthandWithOneArg = - (isObjectTypePropertyAFunction(parent) || - isTypeAnnotationAFunction(parent) || + (isObjectTypePropertyAFunction(parent, options) || + isTypeAnnotationAFunction(parent, options) || parent.type === "TypeAlias" || parent.type === "UnionTypeAnnotation" || parent.type === "TSUnionType" || @@ -3285,6 +3451,9 @@ function printFunctionParams(path, print, options, expandArg, printTypeParams) { !fun.rest; if (isFlowShorthandWithOneArg) { + if (options.arrowParens === "always") { + return concat(["(", concat(printed), ")"]); + } return concat(printed); } @@ -3411,7 +3580,7 @@ function printReturnType(path, print, options) { if ( n.returnType && - isFlowAnnotationComment(options.originalText, n.returnType) + isFlowAnnotationComment(options.originalText, n.returnType, options) ) { return concat([" /*: ", returnType, " */"]); } @@ -3437,7 +3606,9 @@ function printExportDeclaration(path, options, print) { const semi = options.semi ? ";" : ""; const parts = ["export "]; - if (decl["default"] || decl.type === "ExportDefaultDeclaration") { + const isDefault = decl["default"] || decl.type === "ExportDefaultDeclaration"; + + if (isDefault) { parts.push("default "); } @@ -3445,14 +3616,20 @@ function printExportDeclaration(path, options, print) { comments.printDanglingComments(path, options, /* sameIndent */ true) ); + if (needsHardlineAfterDanglingComment(decl)) { + parts.push(hardline); + } + if (decl.declaration) { parts.push(path.call(print, "declaration")); if ( - decl.type === "ExportDefaultDeclaration" && + isDefault && (decl.declaration.type !== "ClassDeclaration" && decl.declaration.type !== "FunctionDeclaration" && - decl.declaration.type !== "TSAbstractClassDeclaration") + decl.declaration.type !== "TSAbstractClassDeclaration" && + decl.declaration.type !== "DeclareClass" && + decl.declaration.type !== "DeclareFunction") ) { parts.push(semi); } @@ -3517,7 +3694,7 @@ function printExportDeclaration(path, options, print) { } function printFlowDeclaration(path, parts) { - const parentExportDecl = util.getParentExportDeclaration(path); + const parentExportDecl = privateUtil.getParentExportDeclaration(path); if (parentExportDecl) { assert.strictEqual(parentExportDecl.type, "DeclareExportDeclaration"); @@ -3632,24 +3809,41 @@ function printClass(path, options, print) { const partsGroup = []; if (n.superClass) { - if (hasLeadingOwnLineComment(options.originalText, n.superClass)) { - parts.push(hardline); - } else { - parts.push(" "); - } - const printed = concat([ "extends ", path.call(print, "superClass"), path.call(print, "superTypeParameters") ]); - parts.push( - path.call( - superClass => - comments.printComments(superClass, () => printed, options), - "superClass" - ) - ); + // Keep old behaviour of extends in same line + // If there is only on extends and there are not comments + if ( + (!n.implements || n.implements.length === 0) && + (!n.superClass.comments || n.superClass.comments.length === 0) + ) { + parts.push( + concat([ + " ", + path.call( + superClass => + comments.printComments(superClass, () => printed, options), + "superClass" + ) + ]) + ); + } else { + partsGroup.push( + group( + concat([ + line, + path.call( + superClass => + comments.printComments(superClass, () => printed, options), + "superClass" + ) + ]) + ) + ); + } } else if (n.extends && n.extends.length > 0) { parts.push(" extends ", join(", ", path.map(print, "extends"))); } @@ -3657,8 +3851,15 @@ function printClass(path, options, print) { if (n["implements"] && n["implements"].length > 0) { partsGroup.push( line, - "implements ", - group(indent(join(concat([",", line]), path.map(print, "implements")))) + "implements", + group( + indent( + concat([ + line, + join(concat([",", line]), path.map(print, "implements")) + ]) + ) + ) ); } @@ -3677,7 +3878,7 @@ function printClass(path, options, print) { if ( n.body && n.body.comments && - hasLeadingOwnLineComment(options.originalText, n.body) + hasLeadingOwnLineComment(options.originalText, n.body, options) ) { parts.push(hardline); } else { @@ -3749,19 +3950,24 @@ function printMemberChain(path, options, print) { // the first group whether it is in parentheses or not function shouldInsertEmptyLineAfter(node) { const originalText = options.originalText; - const nextCharIndex = util.getNextNonSpaceNonCommentCharacterIndex( + const nextCharIndex = sharedUtil.getNextNonSpaceNonCommentCharacterIndex( originalText, - node + node, + options ); const nextChar = originalText.charAt(nextCharIndex); // if it is cut off by a parenthesis, we only account for one typed empty // line after that parenthesis if (nextChar == ")") { - return util.isNextLineEmptyAfterIndex(originalText, nextCharIndex + 1); + return sharedUtil.isNextLineEmptyAfterIndex( + originalText, + nextCharIndex + 1, + options + ); } - return util.isNextLineEmpty(originalText, node); + return sharedUtil.isNextLineEmpty(originalText, node, options); } function rec(path) { @@ -3800,6 +4006,12 @@ function printMemberChain(path, options, print) { ) }); path.call(object => rec(object), "object"); + } else if (node.type === "TSNonNullExpression") { + printedNodes.unshift({ + node: node, + printed: comments.printComments(path, () => "!", options) + }); + path.call(expression => rec(expression), "expression"); } else { printedNodes.unshift({ node: node, @@ -3850,6 +4062,7 @@ function printMemberChain(path, options, print) { let i = 1; for (; i < printedNodes.length; ++i) { if ( + printedNodes[i].node.type === "TSNonNullExpression" || printedNodes[i].node.type === "CallExpression" || (printedNodes[i].node.type === "MemberExpression" && printedNodes[i].node.computed && @@ -3978,7 +4191,7 @@ function printMemberChain(path, options, print) { // Find out the last node in the first group and check if it has an // empty line after - const lastNodeBeforeIndent = util.getLast( + const lastNodeBeforeIndent = privateUtil.getLast( shouldMerge ? groups.slice(1, 2)[0] : groups[0] ).node; const shouldHaveEmptyLineBeforeIndent = @@ -4203,7 +4416,7 @@ function printJSXChildren(path, options, print, jsxWhitespace) { let endWhitespace; // Ends with whitespace - if (util.getLast(words) === "") { + if (privateUtil.getLast(words) === "") { words.pop(); endWhitespace = words.pop(); } @@ -4391,7 +4604,8 @@ function printJSXElement(path, options, print) { // Trim trailing lines (or empty strings) while ( children.length && - (isLineNext(util.getLast(children)) || isEmpty(util.getLast(children))) + (isLineNext(privateUtil.getLast(children)) || + isEmpty(privateUtil.getLast(children))) ) { children.pop(); } @@ -4474,6 +4688,7 @@ function maybeWrapJSXElementInParens(path, elem) { const NO_WRAP_PARENTS = { ArrayExpression: true, + JSXAttribute: true, JSXElement: true, JSXExpressionContainer: true, JSXFragment: true, @@ -4562,7 +4777,7 @@ function printBinaryishExpressions( // precedence level and should be treated as a separate group, so // print them normally. (This doesn't hold for the `**` operator, // which is unique in that it is right-associative.) - if (util.shouldFlatten(node.operator, node.left.operator)) { + if (privateUtil.shouldFlatten(node.operator, node.left.operator)) { // Flatten them out by recursively calling this function. parts = parts.concat( path.call( @@ -4619,7 +4834,7 @@ function printBinaryishExpressions( } function printAssignmentRight(rightNode, printedRight, canBreak, options) { - if (hasLeadingOwnLineComment(options.originalText, rightNode)) { + if (hasLeadingOwnLineComment(options.originalText, rightNode, options)) { return indent(concat([hardline, printedRight])); } @@ -4678,7 +4893,7 @@ function nodeStr(node, options, isFlowOrTypeScriptDirectiveLiteral) { const raw = rawText(node); const isDirectiveLiteral = isFlowOrTypeScriptDirectiveLiteral || node.type === "DirectiveLiteral"; - return util.printString(raw, options, isDirectiveLiteral); + return privateUtil.printString(raw, options, isDirectiveLiteral); } function printRegex(node) { @@ -4709,15 +4924,16 @@ function hasTrailingComment(node) { return node.comments && node.comments.some(comment => comment.trailing); } -function hasLeadingOwnLineComment(text, node) { +function hasLeadingOwnLineComment(text, node, options) { if (isJSXNode(node)) { - return util.hasNodeIgnoreComment(node); + return privateUtil.hasNodeIgnoreComment(node); } const res = node.comments && node.comments.some( - comment => comment.leading && util.hasNewline(text, util.locEnd(comment)) + comment => + comment.leading && privateUtil.hasNewline(text, options.locEnd(comment)) ); return res; } @@ -4737,9 +4953,9 @@ function hasNakedLeftSide(node) { ); } -function isFlowAnnotationComment(text, typeAnnotation) { - const start = util.locStart(typeAnnotation); - const end = util.skipWhitespace(text, util.locEnd(typeAnnotation)); +function isFlowAnnotationComment(text, typeAnnotation, options) { + const start = options.locStart(typeAnnotation); + const end = privateUtil.skipWhitespace(text, options.locEnd(typeAnnotation)); return text.substr(start, 2) === "/*" && text.substr(end, 2) === "*/"; } @@ -4790,7 +5006,7 @@ function exprNeedsASIProtection(path, options) { const node = path.getValue(); const maybeASIProblem = - path.needsParens(options) || + pathNeedsParens(path, options) || node.type === "ParenthesizedExpression" || node.type === "TypeCastExpression" || (node.type === "ArrowFunctionExpression" && @@ -4902,7 +5118,7 @@ function classChildNeedsASIProtection(node) { // (the leftmost leaf node) and, if it (or its parents) has any // leadingComments, returns true (so it can be wrapped in parens). function returnArgumentHasLeadingComment(options, argument) { - if (hasLeadingOwnLineComment(options.originalText, argument)) { + if (hasLeadingOwnLineComment(options.originalText, argument, options)) { return true; } @@ -4912,7 +5128,7 @@ function returnArgumentHasLeadingComment(options, argument) { while ((newLeftMost = getLeftSide(leftMost))) { leftMost = newLeftMost; - if (hasLeadingOwnLineComment(options.originalText, leftMost)) { + if (hasLeadingOwnLineComment(options.originalText, leftMost, options)) { return true; } } @@ -4934,38 +5150,38 @@ function isMemberExpressionChain(node) { // Hack to differentiate between the following two which have the same ast // type T = { method: () => void }; // type T = { method(): void }; -function isObjectTypePropertyAFunction(node) { +function isObjectTypePropertyAFunction(node, options) { return ( node.type === "ObjectTypeProperty" && node.value.type === "FunctionTypeAnnotation" && !node.static && - !isFunctionNotation(node) + !isFunctionNotation(node, options) ); } // TODO: This is a bad hack and we need a better way to distinguish between // arrow functions and otherwise -function isFunctionNotation(node) { - return isGetterOrSetter(node) || sameLocStart(node, node.value); +function isFunctionNotation(node, options) { + return isGetterOrSetter(node) || sameLocStart(node, node.value, options); } function isGetterOrSetter(node) { return node.kind === "get" || node.kind === "set"; } -function sameLocStart(nodeA, nodeB) { - return util.locStart(nodeA) === util.locStart(nodeB); +function sameLocStart(nodeA, nodeB, options) { + return options.locStart(nodeA) === options.locStart(nodeB); } // Hack to differentiate between the following two which have the same ast // declare function f(a): void; // var f: (a) => void; -function isTypeAnnotationAFunction(node) { +function isTypeAnnotationAFunction(node, options) { return ( (node.type === "TypeAnnotation" || node.type === "TSTypeAnnotation") && node.typeAnnotation.type === "FunctionTypeAnnotation" && !node.static && - !sameLocStart(node, node.typeAnnotation) + !sameLocStart(node, node.typeAnnotation, options) ); } @@ -4975,7 +5191,7 @@ function isNodeStartingWithDeclare(node, options) { } return ( options.originalText - .slice(0, util.locStart(node)) + .slice(0, options.locStart(node)) .match(/declare[ \t]*$/) || options.originalText .slice(node.range[0], node.range[1]) @@ -5045,12 +5261,12 @@ function templateLiteralHasNewLines(template) { return template.quasis.some(quasi => quasi.value.raw.includes("\n")); } -function isTemplateOnItsOwnLine(n, text) { +function isTemplateOnItsOwnLine(n, text, options) { return ( ((n.type === "TemplateLiteral" && templateLiteralHasNewLines(n)) || (n.type === "TaggedTemplateExpression" && templateLiteralHasNewLines(n.quasi))) && - !util.hasNewline(text, util.locStart(n), { backwards: true }) + !privateUtil.hasNewline(text, options.locStart(n), { backwards: true }) ); } @@ -5065,7 +5281,11 @@ function printArrayItems(path, options, printPath, print) { separatorParts = [",", line]; if ( childPath.getValue() && - util.isNextLineEmpty(options.originalText, childPath.getValue()) + sharedUtil.isNextLineEmpty( + options.originalText, + childPath.getValue(), + options + ) ) { separatorParts.push(softline); } @@ -5081,6 +5301,18 @@ function hasDanglingComments(node) { ); } +function needsHardlineAfterDanglingComment(node) { + if (!node.comments) { + return false; + } + const lastDanglingComment = privateUtil.getLast( + node.comments.filter(comment => !comment.leading && !comment.trailing) + ); + return ( + lastDanglingComment && !privateUtil.isBlockComment(lastDanglingComment) + ); +} + function isLiteral(node) { return ( node.type === "BooleanLiteral" || @@ -5115,24 +5347,76 @@ function isObjectType(n) { } // eg; `describe("some string", (done) => {})` -function isTestCall(n) { - const unitTestRe = /^(f|x)?(it|describe|test)$/; +function isTestCall(n, parent) { + const unitTestRe = /^(skip|(f|x)?(it|describe|test))$/; + + if (n.arguments.length === 1) { + if ( + n.callee.type === "Identifier" && + n.callee.name === "async" && + parent && + parent.type === "CallExpression" && + isTestCall(parent) + ) { + return isFunctionOrArrowExpression(n.arguments[0].type); + } + + if (isUnitTestSetUp(n)) { + return ( + isFunctionOrArrowExpression(n.arguments[0].type) || + isIdentiferAsync(n.arguments[0]) + ); + } + } else if (n.arguments.length === 2) { + if ( + ((n.callee.type === "Identifier" && unitTestRe.test(n.callee.name)) || + isSkipOrOnlyBlock(n)) && + (isTemplateLiteral(n.arguments[0]) || isStringLiteral(n.arguments[0])) + ) { + return ( + (isFunctionOrArrowExpression(n.arguments[1].type) && + n.arguments[1].params.length <= 1) || + isIdentiferAsync(n.arguments[1]) + ); + } + } + return false; +} + +function isSkipOrOnlyBlock(node) { + const unitTestRe = /^(skip|(f|x)?(it|describe|test))$/; return ( - ((n.callee.type === "Identifier" && unitTestRe.test(n.callee.name)) || - (n.callee.type === "MemberExpression" && - n.callee.object.type === "Identifier" && - n.callee.property.type === "Identifier" && - unitTestRe.test(n.callee.object.name) && - (n.callee.property.name === "only" || - n.callee.property.name === "skip"))) && - n.arguments.length === 2 && - (n.arguments[0].type === "StringLiteral" || - n.arguments[0].type === "TemplateLiteral" || - (n.arguments[0].type === "Literal" && - typeof n.arguments[0].value === "string")) && - (n.arguments[1].type === "FunctionExpression" || - n.arguments[1].type === "ArrowFunctionExpression") && - n.arguments[1].params.length <= 1 + node.callee.type === "MemberExpression" && + node.callee.object.type === "Identifier" && + node.callee.property.type === "Identifier" && + unitTestRe.test(node.callee.object.name) && + (node.callee.property.name === "only" || + node.callee.property.name === "skip") + ); +} + +function isTemplateLiteral(node) { + return node.type === "TemplateLiteral"; +} + +function isIdentiferAsync(node) { + return ( + node.type === "CallExpression" && + node.callee.type === "Identifier" && + node.callee.name === "async" + ); +} + +function isFunctionOrArrowExpression(type) { + return type === "FunctionExpression" || type === "ArrowFunctionExpression"; +} + +function isUnitTestSetUp(n) { + const unitTestSetUpRe = /^(before|after)(Each|All)$/; + return ( + n.callee.type === "Identifier" && + unitTestSetUpRe.test(n.callee.name) && + n.arguments.length === 1 ); } @@ -5166,7 +5450,7 @@ function willPrintOwnComments(path) { ((parent.type === "ClassDeclaration" || parent.type === "ClassExpression") && parent.superClass === node)))) && - !util.hasIgnoreComment(path) + !privateUtil.hasIgnoreComment(path) ); } @@ -5195,14 +5479,16 @@ function printComment(commentPath, options) { } const isInsideFlowComment = - options.originalText.substr(util.locEnd(comment) - 3, 3) === "*-/"; + options.originalText.substr(options.locEnd(comment) - 3, 3) === "*-/"; return "/*" + comment.value + (isInsideFlowComment ? "*-/" : "*/"); } case "CommentLine": case "Line": // Print shebangs with the proper comment characters - if (options.originalText.slice(util.locStart(comment)).startsWith("#!")) { + if ( + options.originalText.slice(options.locStart(comment)).startsWith("#!") + ) { return "#!" + comment.value.trimRight(); } return "//" + comment.value.trimRight(); @@ -5237,12 +5523,17 @@ function printJsDocComment(comment) { } module.exports = { - options: printerOptions, print: genericPrint, embed, + insertPragma, massageAstNode: clean, hasPrettierIgnore, willPrintOwnComments, canAttachComment, - printComment + printComment, + handleComments: { + ownLine: handleComments.handleOwnLineComment, + endOfLine: handleComments.handleEndOfLineComment, + remaining: handleComments.handleRemainingComment + } }; diff --git a/src/language-markdown/embed.js b/src/language-markdown/embed.js index 85fdc360..ee8f25e9 100644 --- a/src/language-markdown/embed.js +++ b/src/language-markdown/embed.js @@ -5,33 +5,42 @@ const support = require("../common/support"); const doc = require("../doc"); const docBuilders = doc.builders; const hardline = docBuilders.hardline; +const literalline = docBuilders.literalline; const concat = docBuilders.concat; +const markAsRoot = docBuilders.markAsRoot; function embed(path, print, textToDoc, options) { const node = path.getValue(); if (node.type === "code") { - const parser = getParserName(node.lang); + // only look for the first string so as to support [markdown-preview-enhanced](https://shd101wyy.github.io/markdown-preview-enhanced/#/code-chunk) + const lang = node.lang.split(/\s/, 1)[0]; + const parser = getParserName(lang); if (parser) { const styleUnit = options.__inJsTemplate ? "~" : "`"; const style = styleUnit.repeat( Math.max(3, util.getMaxContinuousCount(node.value, styleUnit) + 1) ); const doc = textToDoc(node.value, { parser }); - return concat([ - style, - node.lang, - hardline, - replaceNewlinesWithHardlines(doc), - style - ]); + return markAsRoot( + concat([ + style, + node.lang, + hardline, + replaceNewlinesWithLiterallines(doc), + style + ]) + ); } } return null; function getParserName(lang) { - const supportInfo = support.getSupportInfo(undefined, options); + const supportInfo = support.getSupportInfo(null, { + plugins: options.plugins, + pluginsLoaded: true + }); const language = supportInfo.languages.find( language => language.name.toLowerCase() === lang || @@ -45,7 +54,7 @@ function embed(path, print, textToDoc, options) { return null; } - function replaceNewlinesWithHardlines(doc) { + function replaceNewlinesWithLiterallines(doc) { return util.mapDoc( doc, currentDoc => @@ -53,7 +62,7 @@ function embed(path, print, textToDoc, options) { ? concat( currentDoc .split(/(\n)/g) - .map((v, i) => (i % 2 === 0 ? v : hardline)) + .map((v, i) => (i % 2 === 0 ? v : literalline)) ) : currentDoc ); diff --git a/src/language-markdown/index.js b/src/language-markdown/index.js index d287c46b..2d537302 100644 --- a/src/language-markdown/index.js +++ b/src/language-markdown/index.js @@ -1,6 +1,8 @@ "use strict"; const printer = require("./printer-markdown"); +const options = require("./options"); +const pragma = require("./pragma"); // Based on: // https://github.com/github/linguist/blob/master/lib/linguist/languages.yml @@ -37,7 +39,10 @@ const remark = { get parse() { return eval("require")("./parser-markdown"); }, - astFormat: "mdast" + astFormat: "mdast", + hasPragma: pragma.hasPragma, + locStart: node => node.position.start.offset, + locEnd: node => node.position.end.offset }; const parsers = { @@ -52,6 +57,7 @@ const printers = { module.exports = { languages, + options, parsers, printers }; diff --git a/src/language-markdown/pragma.js b/src/language-markdown/pragma.js new file mode 100644 index 00000000..e8862e66 --- /dev/null +++ b/src/language-markdown/pragma.js @@ -0,0 +1,38 @@ +"use strict"; + +const pragmas = ["format", "prettier"]; + +function startWithPragma(text) { + const pragma = `@(${pragmas.join("|")})`; + const regex = new RegExp( + [ + ``, + `` + ].join("|"), + "m" + ); + const matched = text.match(regex); + return matched && matched.index === 0; +} + +function extract(text) { + // yaml (---) and toml (+++) + const matched = text.match( + /^((---|\+\+\+)(?:\n[\s\S]*?\n|\n)\2(?:\n|$))?([\s\S]*)/ + ); + const frontMatter = matched[1]; + const mainContent = matched[3]; + return { frontMatter, mainContent }; +} + +module.exports = { + startWithPragma, + hasPragma: text => startWithPragma(extract(text).mainContent.trimLeft()), + insertPragma: text => { + const extracted = extract(text); + const pragma = ``; + return extracted.frontMatter + ? `${extracted.frontMatter}\n\n${pragma}\n\n${extracted.mainContent}` + : `${pragma}\n\n${extracted.mainContent}`; + } +}; diff --git a/src/language-markdown/printer-markdown.js b/src/language-markdown/printer-markdown.js index 497eb349..6bb4a75f 100644 --- a/src/language-markdown/printer-markdown.js +++ b/src/language-markdown/printer-markdown.js @@ -1,7 +1,8 @@ "use strict"; -const util = require("../common/util"); +const privateUtil = require("../common/util"); const embed = require("./embed"); +const pragma = require("./pragma"); const doc = require("../doc"); const docBuilders = doc.builders; const concat = docBuilders.concat; @@ -11,15 +12,10 @@ const hardline = docBuilders.hardline; const softline = docBuilders.softline; const fill = docBuilders.fill; const align = docBuilders.align; +const group = docBuilders.group; const printDocToString = doc.printer.printDocToString; -const printerOptions = require("./options"); -const SINGLE_LINE_NODE_TYPES = [ - "heading", - "tableCell", - "footnoteDefinition", - "link" -]; +const SINGLE_LINE_NODE_TYPES = ["heading", "tableCell", "link"]; const SIBLING_NODE_TYPES = ["listItem", "definition", "footnoteDefinition"]; @@ -51,7 +47,7 @@ function genericPrint(path, options, print) { if (shouldRemainTheSameContent(path)) { return concat( - util + privateUtil .splitText( options.originalText.slice( node.position.start.offset, @@ -69,10 +65,7 @@ function genericPrint(path, options, print) { switch (node.type) { case "root": - return concat([ - normalizeDoc(printChildren(path, options, print)), - hardline - ]); + return concat([normalizeDoc(printRoot(path, options, print)), hardline]); case "paragraph": return printChildren(path, options, print, { postprocessor: fill @@ -85,8 +78,8 @@ function genericPrint(path, options, print) { .replace( new RegExp( [ - `(^|[${util.punctuationCharRange}])(_+)`, - `(_+)([${util.punctuationCharRange}]|$)` + `(^|[${privateUtil.punctuationCharRange}])(_+)`, + `(_+)([${privateUtil.punctuationCharRange}]|$)` ].join("|"), "g" ), @@ -118,8 +111,8 @@ function genericPrint(path, options, print) { (prevNode && prevNode.type === "sentence" && prevNode.children.length > 0 && - util.getLast(prevNode.children).type === "word" && - !util.getLast(prevNode.children).hasTrailingPunctuation) || + privateUtil.getLast(prevNode.children).type === "word" && + !privateUtil.getLast(prevNode.children).hasTrailingPunctuation) || (nextNode && nextNode.type === "sentence" && nextNode.children.length > 0 && @@ -134,7 +127,7 @@ function genericPrint(path, options, print) { case "delete": return concat(["~~", printChildren(path, options, print), "~~"]); case "inlineCode": { - const backtickCount = util.getMaxContinuousCount(node.value, "`"); + const backtickCount = privateUtil.getMaxContinuousCount(node.value, "`"); const style = backtickCount === 1 ? "``" : "`"; const gap = backtickCount ? " " : ""; return concat([style, gap, node.value, gap, style]); @@ -195,7 +188,10 @@ function genericPrint(path, options, print) { // fenced code block const styleUnit = options.__inJsTemplate ? "~" : "`"; const style = styleUnit.repeat( - Math.max(3, util.getMaxContinuousCount(node.value, styleUnit) + 1) + Math.max( + 3, + privateUtil.getMaxContinuousCount(node.value, styleUnit) + 1 + ) ); return concat([ style, @@ -207,15 +203,20 @@ function genericPrint(path, options, print) { ]); } case "yaml": - return concat(["---", hardline, node.value, hardline, "---"]); - case "toml": - return concat(["+++", hardline, node.value, hardline, "+++"]); + case "toml": { + const style = node.type === "yaml" ? "---" : "+++"; + return node.value + ? concat([style, hardline, node.value, hardline, style]) + : concat([style, hardline, style]); + } case "html": { const parentNode = path.getParentNode(); - return parentNode.type === "root" && - util.getLast(parentNode.children) === node - ? node.value.trimRight() - : node.value; + return replaceNewlinesWithHardlines( + parentNode.type === "root" && + privateUtil.getLast(parentNode.children) === node + ? node.value.trimRight() + : node.value + ); } case "list": { const nthSiblingIndex = getNthListSiblingIndex( @@ -235,32 +236,34 @@ function genericPrint(path, options, print) { return printChildren(path, options, print, { processor: (childPath, index) => { - const prefix = node.ordered - ? (index === 0 - ? node.start - : isGitDiffFriendlyOrderedList ? 1 : node.start + index) + - (nthSiblingIndex % 2 === 0 ? ". " : ") ") - : nthSiblingIndex % 2 === 0 ? "* " : "- "; + const prefix = getPrefix(); return concat([ prefix, - align(" ".repeat(prefix.length), childPath.call(print)) + align( + " ".repeat(prefix.length), + printListItem(childPath, options, print, prefix) + ) ]); + + function getPrefix() { + const rawPrefix = node.ordered + ? (index === 0 + ? node.start + : isGitDiffFriendlyOrderedList ? 1 : node.start + index) + + (nthSiblingIndex % 2 === 0 ? ". " : ") ") + : nthSiblingIndex % 2 === 0 ? "* " : "- "; + + // do not print trailing spaces for empty list item since it might be treated as `break` node + // by [doc-printer](https://github.com/prettier/prettier/blob/1.10.2/src/doc/doc-printer.js#L395-L405), + // we don't want to preserve unnecessary trailing spaces. + const listItem = childPath.getValue(); + return listItem.children.length + ? alignListPrefix(rawPrefix, options) + : rawPrefix; + } } }); } - case "listItem": { - const prefix = - node.checked === null ? "" : node.checked ? "[x] " : "[ ] "; - return concat([ - prefix, - printChildren(path, options, print, { - processor: (childPath, index) => - index === 0 && childPath.getValue().type !== "list" - ? align(" ".repeat(prefix.length), childPath.call(print)) - : childPath.call(print) - }) - ]); - } case "thematicBreak": { const counter = getAncestorCounter(path, "list"); if (counter === -1) { @@ -284,7 +287,7 @@ function genericPrint(path, options, print) { case "imageReference": switch (node.referenceType) { case "full": - return concat(["![", node.alt, "][", node.identifier, "]"]); + return concat(["![", node.alt || "", "][", node.identifier, "]"]); default: return concat([ "![", @@ -305,13 +308,28 @@ function genericPrint(path, options, print) { return concat(["[^", printChildren(path, options, print), "]"]); case "footnoteReference": return concat(["[^", node.identifier, "]"]); - case "footnoteDefinition": + case "footnoteDefinition": { + const nextNode = path.getParentNode().children[path.getName() + 1]; return concat([ "[^", node.identifier, "]: ", - printChildren(path, options, print) + group( + concat([ + align( + " ".repeat(options.tabWidth), + printChildren(path, options, print, { + processor: (childPath, index) => + index === 0 + ? group(concat([softline, softline, childPath.call(print)])) + : childPath.call(print) + }) + ), + nextNode && nextNode.type === "footnoteDefinition" ? softline : "" + ]) + ) ]); + } case "table": return printTable(path, options, print); case "tableCell": @@ -324,11 +342,47 @@ function genericPrint(path, options, print) { hardline ]); case "tableRow": // handled in "table" + case "listItem": // handled in "list" default: throw new Error(`Unknown markdown type ${JSON.stringify(node.type)}`); } } +function printListItem(path, options, print, listPrefix) { + const node = path.getValue(); + const prefix = node.checked === null ? "" : node.checked ? "[x] " : "[ ] "; + return concat([ + prefix, + printChildren(path, options, print, { + processor: (childPath, index) => { + if (index === 0 && childPath.getValue().type !== "list") { + return align(" ".repeat(prefix.length), childPath.call(print)); + } + + const alignment = " ".repeat( + clamp(options.tabWidth - listPrefix.length, 0, 3) // 4+ will cause indented code block + ); + return concat([alignment, align(alignment, childPath.call(print))]); + } + }) + ]); +} + +function alignListPrefix(prefix, options) { + const additionalSpaces = getAdditionalSpaces(); + return ( + prefix + + " ".repeat( + additionalSpaces >= 4 ? 0 : additionalSpaces // 4+ will cause indented code block + ) + ); + + function getAdditionalSpaces() { + const restSpaces = prefix.length % options.tabWidth; + return restSpaces === 0 ? 0 : options.tabWidth - restSpaces; + } +} + function getNthListSiblingIndex(node, parentNode) { return getNthSiblingIndex( node, @@ -337,6 +391,10 @@ function getNthListSiblingIndex(node, parentNode) { ); } +function replaceNewlinesWithHardlines(str) { + return join(hardline, str.split("\n")); +} + function getNthSiblingIndex(node, parentNode, condition) { condition = condition || (() => true); @@ -407,7 +465,7 @@ function printTable(path, options, print) { const columnMaxWidths = contents.reduce( (currentWidths, rowContents) => currentWidths.map((width, columnIndex) => - Math.max(width, util.getStringWidth(rowContents[columnIndex])) + Math.max(width, privateUtil.getStringWidth(rowContents[columnIndex])) ), contents[0].map(() => 3) // minimum width = 3 (---, :--, :-:, --:) ); @@ -461,21 +519,83 @@ function printTable(path, options, print) { } function alignLeft(text, width) { - return concat([text, " ".repeat(width - util.getStringWidth(text))]); + return concat([text, " ".repeat(width - privateUtil.getStringWidth(text))]); } function alignRight(text, width) { - return concat([" ".repeat(width - util.getStringWidth(text)), text]); + return concat([" ".repeat(width - privateUtil.getStringWidth(text)), text]); } function alignCenter(text, width) { - const spaces = width - util.getStringWidth(text); + const spaces = width - privateUtil.getStringWidth(text); const left = Math.floor(spaces / 2); const right = spaces - left; return concat([" ".repeat(left), text, " ".repeat(right)]); } } +function printRoot(path, options, print) { + /** @typedef {{ index: number, offset: number }} IgnorePosition */ + /** @type {Array<{start: IgnorePosition, end: IgnorePosition}>} */ + const ignoreRanges = []; + + /** @type {IgnorePosition | null} */ + let ignoreStart = null; + + const children = path.getValue().children; + children.forEach((childNode, index) => { + switch (isPrettierIgnore(childNode)) { + case "start": + if (ignoreStart === null) { + ignoreStart = { index, offset: childNode.position.end.offset }; + } + break; + case "end": + if (ignoreStart !== null) { + ignoreRanges.push({ + start: ignoreStart, + end: { index, offset: childNode.position.start.offset } + }); + ignoreStart = null; + } + break; + default: + // do nothing + break; + } + }); + + return printChildren(path, options, print, { + processor: (childPath, index) => { + if (ignoreRanges.length !== 0) { + const ignoreRange = ignoreRanges[0]; + + if (index === ignoreRange.start.index) { + return concat([ + children[ignoreRange.start.index].value, + options.originalText.slice( + ignoreRange.start.offset, + ignoreRange.end.offset + ), + children[ignoreRange.end.index].value + ]); + } + + if (ignoreRange.start.index < index && index < ignoreRange.end.index) { + return false; + } + + if (index === ignoreRange.end.index) { + ignoreRanges.shift(); + return false; + } + } + + return childPath.call(print); + } + }); +} + function printChildren(path, options, print, events) { events = events || {}; @@ -485,28 +605,15 @@ function printChildren(path, options, print, events) { const node = path.getValue(); const parts = []; - let counter = 0; let lastChildNode; - let prettierIgnore = false; path.map((childPath, index) => { const childNode = childPath.getValue(); - const result = prettierIgnore - ? options.originalText.slice( - childNode.position.start.offset, - childNode.position.end.offset - ) - : processor(childPath, index); - - prettierIgnore = false; - + const result = processor(childPath, index); if (result !== false) { - prettierIgnore = isPrettierIgnore(childNode); - const data = { parts, - index: counter++, prevNode: lastChildNode, parentNode: node, options @@ -536,10 +643,15 @@ function printChildren(path, options, print, events) { return postprocessor(parts); } +/** @return {false | 'next' | 'start' | 'end'} */ function isPrettierIgnore(node) { - return ( - node.type === "html" && /^$/.test(node.value) + if (node.type !== "html") { + return false; + } + const match = node.value.match( + /^$/ ); + return match === null ? false : match[1] ? match[1] : "next"; } function shouldNotPrePrintHardline(node, data) { @@ -564,7 +676,7 @@ function shouldPrePrintDoubleHardline(node, data) { const isPrevNodeLooseListItem = data.prevNode && data.prevNode.type === "listItem" && data.prevNode.loose; - const isPrevNodePrettierIgnore = isPrettierIgnore(data.prevNode); + const isPrevNodePrettierIgnore = isPrettierIgnore(data.prevNode) === "next"; return ( isPrevNodeLooseListItem || @@ -595,7 +707,7 @@ function shouldRemainTheSameContent(path) { } function normalizeDoc(doc) { - return util.mapDoc(doc, currentDoc => { + return privateUtil.mapDoc(doc, currentDoc => { if (!currentDoc.parts) { return currentDoc; } @@ -647,7 +759,7 @@ function printTitle(title, options) { function normalizeParts(parts) { return parts.reduce((current, part) => { - const lastPart = util.getLast(current); + const lastPart = privateUtil.getLast(current); if (typeof lastPart === "string" && typeof part === "string") { current.splice(-1, 1, lastPart + part); @@ -659,21 +771,49 @@ function normalizeParts(parts) { }, []); } -function clean(ast, newObj) { - // for markdown codeblock +function clamp(value, min, max) { + return value < min ? min : value > max ? max : value; +} + +function clean(ast, newObj, parent) { + // for codeblock if (ast.type === "code") { delete newObj.value; } - // for markdown whitespace: "\n" and " " are considered the same + // for whitespace: "\n" and " " are considered the same if (ast.type === "whitespace" && ast.value === "\n") { newObj.value = " "; } + // for insert pragma + if ( + parent && + parent.type === "root" && + (parent.children[0] === ast || + ((parent.children[0].type === "yaml" || + parent.children[0].type === "toml") && + parent.children[1] === ast)) && + ast.type === "html" && + pragma.startWithPragma(ast.value) + ) { + return null; + } +} + +function hasPrettierIgnore(path) { + const index = +path.getName(); + + if (index === 0) { + return false; + } + + const prevNode = path.getParentNode().children[index - 1]; + return isPrettierIgnore(prevNode) === "next"; } module.exports = { - options: printerOptions, print: genericPrint, embed, massageAstNode: clean, - hasPrettierIgnore: util.hasIgnoreComment + hasPrettierIgnore, + insertPragma: pragma.insertPragma }; diff --git a/src/language-vue/embed.js b/src/language-vue/embed.js index 805b338e..bcb37bee 100644 --- a/src/language-vue/embed.js +++ b/src/language-vue/embed.js @@ -7,7 +7,7 @@ const hardline = docBuilders.hardline; function embed(path, print, textToDoc, options) { const node = path.getValue(); const parent = path.getParentNode(); - if (!parent || parent.tag !== "root") { + if (!parent || parent.tag !== "root" || node.unary) { return null; } @@ -15,7 +15,7 @@ function embed(path, print, textToDoc, options) { if (node.tag === "style") { const langAttr = node.attrs.find(attr => attr.name === "lang"); - if (!langAttr) { + if (!langAttr || langAttr.value === "postcss") { parser = "css"; } else if (langAttr.value === "scss") { parser = "scss"; @@ -28,7 +28,7 @@ function embed(path, print, textToDoc, options) { const langAttr = node.attrs.find(attr => attr.name === "lang"); if (!langAttr) { parser = "babylon"; - } else if (langAttr.value === "ts") { + } else if (langAttr.value === "ts" || langAttr.value === "tsx") { parser = "typescript"; } } diff --git a/src/language-vue/parser-vue.js b/src/language-vue/parser-vue.js index b1f07a0a..482eca34 100644 --- a/src/language-vue/parser-vue.js +++ b/src/language-vue/parser-vue.js @@ -388,12 +388,16 @@ function parse(text /*, parsers, opts*/) { attrs, unary, start, - contentStart: end, children: [] }; obj.children.push(newObj); - objStack.push(newObj); - obj = newObj; + if (unary) { + newObj.end = end; + } else { + newObj.contentStart = end; + objStack.push(newObj); + obj = newObj; + } }, end: function(tag, start, end) { objStack.pop(); diff --git a/src/language-vue/printer-vue.js b/src/language-vue/printer-vue.js index ca2075ef..c095b1b7 100644 --- a/src/language-vue/printer-vue.js +++ b/src/language-vue/printer-vue.js @@ -3,6 +3,7 @@ const embed = require("./embed"); const docBuilders = require("../doc").builders; const concat = docBuilders.concat; +const hardline = docBuilders.hardline; function genericPrint(path, options, print) { const n = path.getValue(); @@ -15,8 +16,17 @@ function genericPrint(path, options, print) { res.push(childPath.call(print)); index = child.end; }, "children"); + + // If there are no children, we just print the node from start to end. + // Otherwise, index should point to the end of the last child, and we + // need to print the closing tag. res.push(options.originalText.slice(index, n.end)); + // Only force a trailing newline if there were any contents. + if (n.tag === "root" && n.children.length) { + res.push(hardline); + } + return concat(res); } diff --git a/src/main/ast-to-doc.js b/src/main/ast-to-doc.js index 7ebaa540..c15bd433 100644 --- a/src/main/ast-to-doc.js +++ b/src/main/ast-to-doc.js @@ -4,7 +4,6 @@ const assert = require("assert"); const comments = require("./comments"); const FastPath = require("../common/fast-path"); const multiparser = require("./multiparser"); -const util = require("../common/util"); const doc = require("../doc"); const docBuilders = doc.builders; @@ -75,7 +74,10 @@ function genericPrint(path, options, printPath, args) { // Escape hatch if (printer.hasPrettierIgnore && printer.hasPrettierIgnore(path)) { - return options.originalText.slice(util.locStart(node), util.locEnd(node)); + return options.originalText.slice( + options.locStart(node), + options.locEnd(node) + ); } if (node) { diff --git a/src/main/comments.js b/src/main/comments.js index ad2fe68d..4113a925 100644 --- a/src/main/comments.js +++ b/src/main/comments.js @@ -9,20 +9,21 @@ const indent = docBuilders.indent; const lineSuffix = docBuilders.lineSuffix; const join = docBuilders.join; const cursor = docBuilders.cursor; -const util = require("../common/util"); +const privateUtil = require("../common/util"); +const sharedUtil = require("../common/util-shared"); const childNodesCacheKey = Symbol("child-nodes"); -const locStart = util.locStart; -const locEnd = util.locEnd; -const getNextNonSpaceNonCommentCharacter = - util.getNextNonSpaceNonCommentCharacter; -const getNextNonSpaceNonCommentCharacterIndex = - util.getNextNonSpaceNonCommentCharacterIndex; + +const addLeadingComment = sharedUtil.addLeadingComment; +const addTrailingComment = sharedUtil.addTrailingComment; +const addDanglingComment = sharedUtil.addDanglingComment; function getSortedChildNodes(node, text, options, resultArray) { if (!node) { return; } const printer = options.printer; + const locStart = options.locStart; + const locEnd = options.locEnd; if (resultArray) { if (node && printer.canAttachComment && printer.canAttachComment(node)) { @@ -45,13 +46,22 @@ function getSortedChildNodes(node, text, options, resultArray) { return node[childNodesCacheKey]; } - let names; - if (node && typeof node === "object") { - names = Object.keys(node).filter( - n => - n !== "enclosingNode" && n !== "precedingNode" && n !== "followingNode" - ); - } else { + let childNodes; + + if (printer.getCommentChildNodes) { + childNodes = printer.getCommentChildNodes(node); + } else if (node && typeof node === "object") { + childNodes = Object.keys(node) + .filter( + n => + n !== "enclosingNode" && + n !== "precedingNode" && + n !== "followingNode" + ) + .map(n => node[n]); + } + + if (!childNodes) { return; } @@ -62,9 +72,9 @@ function getSortedChildNodes(node, text, options, resultArray) { }); } - for (let i = 0, nameCount = names.length; i < nameCount; ++i) { - getSortedChildNodes(node[names[i]], text, options, resultArray); - } + childNodes.forEach(childNode => { + getSortedChildNodes(childNode, text, options, resultArray); + }); return resultArray; } @@ -73,6 +83,8 @@ function getSortedChildNodes(node, text, options, resultArray) { // .precedingNode, .enclosingNode, and/or .followingNode properties, at // least one of which is guaranteed to be defined. function decorateComment(node, comment, text, options) { + const locStart = options.locStart; + const locEnd = options.locEnd; const childNodes = getSortedChildNodes(node, text, options); let precedingNode; let followingNode; @@ -125,17 +137,23 @@ function decorateComment(node, comment, text, options) { comment.enclosingNode.type === "TemplateLiteral" ) { const quasis = comment.enclosingNode.quasis; - const commentIndex = findExpressionIndexForComment(quasis, comment); + const commentIndex = findExpressionIndexForComment( + quasis, + comment, + options + ); if ( precedingNode && - findExpressionIndexForComment(quasis, precedingNode) !== commentIndex + findExpressionIndexForComment(quasis, precedingNode, options) !== + commentIndex ) { precedingNode = null; } if ( followingNode && - findExpressionIndexForComment(quasis, followingNode) !== commentIndex + findExpressionIndexForComment(quasis, followingNode, options) !== + commentIndex ) { followingNode = null; } @@ -156,6 +174,8 @@ function attach(comments, ast, text, options) { } const tiesToBreak = []; + const locStart = options.locStart; + const locEnd = options.locEnd; comments.forEach((comment, i) => { if (options.parser === "json" && locStart(comment) - locStart(ast) <= 0) { @@ -169,51 +189,26 @@ function attach(comments, ast, text, options) { const enclosingNode = comment.enclosingNode; const followingNode = comment.followingNode; + const pluginHandleOwnLineComment = + options.printer.handleComments && options.printer.handleComments.ownLine + ? options.printer.handleComments.ownLine + : () => false; + const pluginHandleEndOfLineComment = + options.printer.handleComments && options.printer.handleComments.endOfLine + ? options.printer.handleComments.endOfLine + : () => false; + const pluginHandleRemainingComment = + options.printer.handleComments && options.printer.handleComments.remaining + ? options.printer.handleComments.remaining + : () => false; + const isLastComment = comments.length - 1 === i; - if (util.hasNewline(text, locStart(comment), { backwards: true })) { + if (privateUtil.hasNewline(text, locStart(comment), { backwards: true })) { // If a comment exists on its own line, prefer a leading comment. // We also need to check if it's the first line of the file. if ( - handleLastFunctionArgComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment - ) || - handleMemberExpressionComments(enclosingNode, followingNode, comment) || - handleIfStatementComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment - ) || - handleTryStatementComments(enclosingNode, followingNode, comment) || - handleClassComments( - enclosingNode, - precedingNode, - followingNode, - comment - ) || - handleImportSpecifierComments(enclosingNode, comment) || - handleForComments(enclosingNode, precedingNode, comment) || - handleUnionTypeComments( - precedingNode, - enclosingNode, - followingNode, - comment - ) || - handleOnlyComments(enclosingNode, ast, comment, isLastComment) || - handleImportDeclarationComments( - text, - enclosingNode, - precedingNode, - comment - ) || - handleAssignmentPatternComments(enclosingNode, comment) || - handleMethodNameComments(text, enclosingNode, precedingNode, comment) + pluginHandleOwnLineComment(comment, text, options, ast, isLastComment) ) { // We're good } else if (followingNode) { @@ -228,43 +223,9 @@ function attach(comments, ast, text, options) { /* istanbul ignore next */ addDanglingComment(ast, comment); } - } else if (util.hasNewline(text, locEnd(comment))) { + } else if (privateUtil.hasNewline(text, locEnd(comment))) { if ( - handleLastFunctionArgComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment - ) || - handleConditionalExpressionComments( - enclosingNode, - precedingNode, - followingNode, - comment, - text - ) || - handleImportSpecifierComments(enclosingNode, comment) || - handleIfStatementComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment - ) || - handleClassComments( - enclosingNode, - precedingNode, - followingNode, - comment - ) || - handleLabeledStatementComments(enclosingNode, comment) || - handleCallExpressionComments(precedingNode, enclosingNode, comment) || - handlePropertyComments(enclosingNode, comment) || - handleExportNamedDeclarationComments(enclosingNode, comment) || - handleOnlyComments(enclosingNode, ast, comment, isLastComment) || - handleTypeAliasComments(enclosingNode, followingNode, comment) || - handleVariableDeclaratorComments(enclosingNode, followingNode, comment) + pluginHandleEndOfLineComment(comment, text, options, ast, isLastComment) ) { // We're good } else if (precedingNode) { @@ -282,19 +243,7 @@ function attach(comments, ast, text, options) { } } else { if ( - handleIfStatementComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment - ) || - handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) || - handleCommentInEmptyParens(text, enclosingNode, comment) || - handleMethodNameComments(text, enclosingNode, precedingNode, comment) || - handleOnlyComments(enclosingNode, ast, comment, isLastComment) || - handleCommentAfterArrowParams(text, enclosingNode, comment) || - handleFunctionNameComments(text, enclosingNode, precedingNode, comment) + pluginHandleRemainingComment(comment, text, options, ast, isLastComment) ) { // We're good } else if (precedingNode && followingNode) { @@ -307,7 +256,7 @@ function attach(comments, ast, text, options) { if (tieCount > 0) { const lastTie = tiesToBreak[tieCount - 1]; if (lastTie.followingNode !== comment.followingNode) { - breakTies(tiesToBreak, text); + breakTies(tiesToBreak, text, options); } } tiesToBreak.push(comment); @@ -325,7 +274,7 @@ function attach(comments, ast, text, options) { } }); - breakTies(tiesToBreak, text); + breakTies(tiesToBreak, text, options); comments.forEach(comment => { // These node references were useful for breaking ties, but we @@ -337,7 +286,7 @@ function attach(comments, ast, text, options) { }); } -function breakTies(tiesToBreak, text) { +function breakTies(tiesToBreak, text, options) { const tieCount = tiesToBreak.length; if (tieCount === 0) { return; @@ -345,7 +294,7 @@ function breakTies(tiesToBreak, text) { const precedingNode = tiesToBreak[0].precedingNode; const followingNode = tiesToBreak[0].followingNode; - let gapEndPos = locStart(followingNode); + let gapEndPos = options.locStart(followingNode); // Iterate backwards through tiesToBreak, examining the gaps // between the tied comments. In order to qualify as leading, a @@ -362,9 +311,9 @@ function breakTies(tiesToBreak, text) { assert.strictEqual(comment.precedingNode, precedingNode); assert.strictEqual(comment.followingNode, followingNode); - const gap = text.slice(locEnd(comment), gapEndPos).trim(); + const gap = text.slice(options.locEnd(comment), gapEndPos).trim(); if (gap === "" || /^\(+$/.test(gap)) { - gapEndPos = locStart(comment); + gapEndPos = options.locStart(comment); } else { // The gap string contained something other than whitespace or open // parentheses. @@ -383,540 +332,14 @@ function breakTies(tiesToBreak, text) { tiesToBreak.length = 0; } -function addCommentHelper(node, comment) { - const comments = node.comments || (node.comments = []); - comments.push(comment); - comment.printed = false; - - // For some reason, TypeScript parses `// x` inside of JSXText as a comment - // We already "print" it via the raw text, we don't need to re-print it as a - // comment - if (node.type === "JSXText") { - comment.printed = true; - } -} - -function addLeadingComment(node, comment) { - comment.leading = true; - comment.trailing = false; - addCommentHelper(node, comment); -} - -function addDanglingComment(node, comment) { - comment.leading = false; - comment.trailing = false; - addCommentHelper(node, comment); -} - -function addTrailingComment(node, comment) { - comment.leading = false; - comment.trailing = true; - addCommentHelper(node, comment); -} - -function addBlockStatementFirstComment(node, comment) { - const body = node.body.filter(n => n.type !== "EmptyStatement"); - if (body.length === 0) { - addDanglingComment(node, comment); - } else { - addLeadingComment(body[0], comment); - } -} - -function addBlockOrNotComment(node, comment) { - if (node.type === "BlockStatement") { - addBlockStatementFirstComment(node, comment); - } else { - addLeadingComment(node, comment); - } -} - -// There are often comments before the else clause of if statements like -// -// if (1) { ... } -// // comment -// else { ... } -// -// They are being attached as leading comments of the BlockExpression which -// is not well printed. What we want is to instead move the comment inside -// of the block and make it leadingComment of the first element of the block -// or dangling comment of the block if there is nothing inside -// -// if (1) { ... } -// else { -// // comment -// ... -// } -function handleIfStatementComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment -) { - if ( - !enclosingNode || - enclosingNode.type !== "IfStatement" || - !followingNode - ) { - return false; - } - - // We unfortunately have no way using the AST or location of nodes to know - // if the comment is positioned before the condition parenthesis: - // if (a /* comment */) {} - // The only workaround I found is to look at the next character to see if - // it is a ). - const nextCharacter = getNextNonSpaceNonCommentCharacter(text, comment); - if (nextCharacter === ")") { - addTrailingComment(precedingNode, comment); - return true; - } - - if (followingNode.type === "BlockStatement") { - addBlockStatementFirstComment(followingNode, comment); - return true; - } - - if (followingNode.type === "IfStatement") { - addBlockOrNotComment(followingNode.consequent, comment); - return true; - } - - // For comments positioned after the condition parenthesis in an if statement - // before the consequent with or without brackets on, such as - // if (a) /* comment */ {} or if (a) /* comment */ true, - // we look at the next character to see if it is a { or if the following node - // is the consequent for the if statement - if (nextCharacter === "{" || enclosingNode.consequent === followingNode) { - addLeadingComment(followingNode, comment); - return true; - } - - return false; -} - -// Same as IfStatement but for TryStatement -function handleTryStatementComments(enclosingNode, followingNode, comment) { - if ( - !enclosingNode || - enclosingNode.type !== "TryStatement" || - !followingNode - ) { - return false; - } - - if (followingNode.type === "BlockStatement") { - addBlockStatementFirstComment(followingNode, comment); - return true; - } - - if (followingNode.type === "TryStatement") { - addBlockOrNotComment(followingNode.finalizer, comment); - return true; - } - - if (followingNode.type === "CatchClause") { - addBlockOrNotComment(followingNode.body, comment); - return true; - } - - return false; -} - -function handleMemberExpressionComments(enclosingNode, followingNode, comment) { - if ( - enclosingNode && - enclosingNode.type === "MemberExpression" && - followingNode && - followingNode.type === "Identifier" - ) { - addLeadingComment(enclosingNode, comment); - return true; - } - - return false; -} - -function handleConditionalExpressionComments( - enclosingNode, - precedingNode, - followingNode, - comment, - text -) { - const isSameLineAsPrecedingNode = - precedingNode && - !util.hasNewlineInRange(text, locEnd(precedingNode), locStart(comment)); - - if ( - (!precedingNode || !isSameLineAsPrecedingNode) && - enclosingNode && - enclosingNode.type === "ConditionalExpression" && - followingNode - ) { - addLeadingComment(followingNode, comment); - return true; - } - return false; -} - -function handleObjectPropertyAssignment(enclosingNode, precedingNode, comment) { - if ( - enclosingNode && - (enclosingNode.type === "ObjectProperty" || - enclosingNode.type === "Property") && - enclosingNode.shorthand && - enclosingNode.key === precedingNode && - enclosingNode.value.type === "AssignmentPattern" - ) { - addTrailingComment(enclosingNode.value.left, comment); - return true; - } - return false; -} - -function handleClassComments( - enclosingNode, - precedingNode, - followingNode, - comment -) { - if ( - enclosingNode && - (enclosingNode.type === "ClassDeclaration" || - enclosingNode.type === "ClassExpression") && - (enclosingNode.decorators && enclosingNode.decorators.length > 0) && - !(followingNode && followingNode.type === "Decorator") - ) { - if (!enclosingNode.decorators || enclosingNode.decorators.length === 0) { - addLeadingComment(enclosingNode, comment); - } else { - addTrailingComment( - enclosingNode.decorators[enclosingNode.decorators.length - 1], - comment - ); - } - return true; - } - return false; -} - -function handleMethodNameComments(text, enclosingNode, precedingNode, comment) { - // This is only needed for estree parsers (flow, typescript) to attach - // after a method name: - // obj = { fn /*comment*/() {} }; - if ( - enclosingNode && - precedingNode && - (enclosingNode.type === "Property" || - enclosingNode.type === "MethodDefinition") && - precedingNode.type === "Identifier" && - enclosingNode.key === precedingNode && - // special Property case: { key: /*comment*/(value) }; - // comment should be attached to value instead of key - getNextNonSpaceNonCommentCharacter(text, precedingNode) !== ":" - ) { - addTrailingComment(precedingNode, comment); - return true; - } - - // Print comments between decorators and class methods as a trailing comment - // on the decorator node instead of the method node - if ( - precedingNode && - enclosingNode && - precedingNode.type === "Decorator" && - (enclosingNode.type === "ClassMethod" || - enclosingNode.type === "ClassProperty" || - enclosingNode.type === "TSAbstractClassProperty" || - enclosingNode.type === "TSAbstractMethodDefinition" || - enclosingNode.type === "MethodDefinition") - ) { - addTrailingComment(precedingNode, comment); - return true; - } - - return false; -} - -function handleFunctionNameComments( - text, - enclosingNode, - precedingNode, - comment -) { - if (getNextNonSpaceNonCommentCharacter(text, comment) !== "(") { - return false; - } - - if ( - precedingNode && - enclosingNode && - (enclosingNode.type === "FunctionDeclaration" || - enclosingNode.type === "FunctionExpression" || - enclosingNode.type === "ClassMethod" || - enclosingNode.type === "MethodDefinition" || - enclosingNode.type === "ObjectMethod") - ) { - addTrailingComment(precedingNode, comment); - return true; - } - return false; -} - -function handleCommentAfterArrowParams(text, enclosingNode, comment) { - if (!(enclosingNode && enclosingNode.type === "ArrowFunctionExpression")) { - return false; - } - - const index = getNextNonSpaceNonCommentCharacterIndex(text, comment); - if (text.substr(index, 2) === "=>") { - addDanglingComment(enclosingNode, comment); - return true; - } - - return false; -} - -function handleCommentInEmptyParens(text, enclosingNode, comment) { - if (getNextNonSpaceNonCommentCharacter(text, comment) !== ")") { - return false; - } - - // Only add dangling comments to fix the case when no params are present, - // i.e. a function without any argument. - if ( - enclosingNode && - (((enclosingNode.type === "FunctionDeclaration" || - enclosingNode.type === "FunctionExpression" || - (enclosingNode.type === "ArrowFunctionExpression" && - (enclosingNode.body.type !== "CallExpression" || - enclosingNode.body.arguments.length === 0)) || - enclosingNode.type === "ClassMethod" || - enclosingNode.type === "ObjectMethod") && - enclosingNode.params.length === 0) || - (enclosingNode.type === "CallExpression" && - enclosingNode.arguments.length === 0)) - ) { - addDanglingComment(enclosingNode, comment); - return true; - } - if ( - enclosingNode && - (enclosingNode.type === "MethodDefinition" && - enclosingNode.value.params.length === 0) - ) { - addDanglingComment(enclosingNode.value, comment); - return true; - } - return false; -} - -function handleLastFunctionArgComments( - text, - precedingNode, - enclosingNode, - followingNode, - comment -) { - // Type definitions functions - if ( - precedingNode && - precedingNode.type === "FunctionTypeParam" && - enclosingNode && - enclosingNode.type === "FunctionTypeAnnotation" && - followingNode && - followingNode.type !== "FunctionTypeParam" - ) { - addTrailingComment(precedingNode, comment); - return true; - } - - // Real functions - if ( - precedingNode && - (precedingNode.type === "Identifier" || - precedingNode.type === "AssignmentPattern") && - enclosingNode && - (enclosingNode.type === "ArrowFunctionExpression" || - enclosingNode.type === "FunctionExpression" || - enclosingNode.type === "FunctionDeclaration" || - enclosingNode.type === "ObjectMethod" || - enclosingNode.type === "ClassMethod") && - getNextNonSpaceNonCommentCharacter(text, comment) === ")" - ) { - addTrailingComment(precedingNode, comment); - return true; - } - return false; -} - -function handleImportSpecifierComments(enclosingNode, comment) { - if (enclosingNode && enclosingNode.type === "ImportSpecifier") { - addLeadingComment(enclosingNode, comment); - return true; - } - return false; -} - -function handleLabeledStatementComments(enclosingNode, comment) { - if (enclosingNode && enclosingNode.type === "LabeledStatement") { - addLeadingComment(enclosingNode, comment); - return true; - } - return false; -} - -function handleCallExpressionComments(precedingNode, enclosingNode, comment) { - if ( - enclosingNode && - enclosingNode.type === "CallExpression" && - precedingNode && - enclosingNode.callee === precedingNode && - enclosingNode.arguments.length > 0 - ) { - addLeadingComment(enclosingNode.arguments[0], comment); - return true; - } - return false; -} - -function handleUnionTypeComments( - precedingNode, - enclosingNode, - followingNode, - comment -) { - if ( - enclosingNode && - (enclosingNode.type === "UnionTypeAnnotation" || - enclosingNode.type === "TSUnionType") - ) { - addTrailingComment(precedingNode, comment); - return true; - } - return false; -} - -function handlePropertyComments(enclosingNode, comment) { - if ( - enclosingNode && - (enclosingNode.type === "Property" || - enclosingNode.type === "ObjectProperty") - ) { - addLeadingComment(enclosingNode, comment); - return true; - } - return false; -} - -function handleExportNamedDeclarationComments(enclosingNode, comment) { - if (enclosingNode && enclosingNode.type === "ExportNamedDeclaration") { - addLeadingComment(enclosingNode, comment); - return true; - } - return false; -} - -function handleOnlyComments(enclosingNode, ast, comment, isLastComment) { - // With Flow the enclosingNode is undefined so use the AST instead. - if (ast && ast.body && ast.body.length === 0) { - if (isLastComment) { - addDanglingComment(ast, comment); - } else { - addLeadingComment(ast, comment); - } - return true; - } else if ( - enclosingNode && - enclosingNode.type === "Program" && - enclosingNode.body.length === 0 && - enclosingNode.directives && - enclosingNode.directives.length === 0 - ) { - if (isLastComment) { - addDanglingComment(enclosingNode, comment); - } else { - addLeadingComment(enclosingNode, comment); - } - return true; - } - return false; -} - -function handleForComments(enclosingNode, precedingNode, comment) { - if ( - enclosingNode && - (enclosingNode.type === "ForInStatement" || - enclosingNode.type === "ForOfStatement") - ) { - addLeadingComment(enclosingNode, comment); - return true; - } - return false; -} - -function handleImportDeclarationComments( - text, - enclosingNode, - precedingNode, - comment -) { - if ( - precedingNode && - enclosingNode && - enclosingNode.type === "ImportDeclaration" && - util.hasNewline(text, util.locEnd(comment)) - ) { - addTrailingComment(precedingNode, comment); - return true; - } - return false; -} - -function handleAssignmentPatternComments(enclosingNode, comment) { - if (enclosingNode && enclosingNode.type === "AssignmentPattern") { - addLeadingComment(enclosingNode, comment); - return true; - } - return false; -} - -function handleTypeAliasComments(enclosingNode, followingNode, comment) { - if (enclosingNode && enclosingNode.type === "TypeAlias") { - addLeadingComment(enclosingNode, comment); - return true; - } - return false; -} - -function handleVariableDeclaratorComments( - enclosingNode, - followingNode, - comment -) { - if ( - enclosingNode && - enclosingNode.type === "VariableDeclarator" && - followingNode && - (followingNode.type === "ObjectExpression" || - followingNode.type === "ArrayExpression") - ) { - addLeadingComment(followingNode, comment); - return true; - } - return false; -} - function printComment(commentPath, options) { const comment = commentPath.getValue(); comment.printed = true; return options.printer.printComment(commentPath, options); } -function findExpressionIndexForComment(quasis, comment) { - const startPos = locStart(comment) - 1; +function findExpressionIndexForComment(quasis, comment, options) { + const startPos = options.locStart(comment) - 1; for (let i = 1; i < quasis.length; ++i) { if (startPos < getQuasiRange(quasis[i]).start) { @@ -945,14 +368,16 @@ function printLeadingComment(commentPath, print, options) { if (!contents) { return ""; } - const isBlock = util.isBlockComment(comment); + const isBlock = privateUtil.isBlockComment(comment); // Leading block comments should see if they need to stay on the // same line or not. if (isBlock) { return concat([ contents, - util.hasNewline(options.originalText, locEnd(comment)) ? hardline : " " + privateUtil.hasNewline(options.originalText, options.locEnd(comment)) + ? hardline + : " " ]); } @@ -965,10 +390,21 @@ function printTrailingComment(commentPath, print, options) { if (!contents) { return ""; } - const isBlock = util.isBlockComment(comment); + const isBlock = privateUtil.isBlockComment(comment); + + // We don't want the line to break + // when the parentParentNode is a ClassDeclaration/-Expression + // And the parentNode is in the superClass property + const parentNode = commentPath.getNode(1); + const parentParentNode = commentPath.getNode(2); + const isParentSuperClass = + parentParentNode && + (parentParentNode.type === "ClassDeclaration" || + parentParentNode.type === "ClassExpression") && + parentParentNode.superClass === parentNode; if ( - util.hasNewline(options.originalText, locStart(comment), { + privateUtil.hasNewline(options.originalText, options.locStart(comment), { backwards: true }) ) { @@ -984,15 +420,16 @@ function printTrailingComment(commentPath, print, options) { // if this a comment on its own line; normal trailing comments are // always at the end of another expression. - const isLineBeforeEmpty = util.isPreviousLineEmpty( + const isLineBeforeEmpty = privateUtil.isPreviousLineEmpty( options.originalText, - comment + comment, + options.locStart ); return lineSuffix( concat([hardline, isLineBeforeEmpty ? hardline : "", contents]) ); - } else if (isBlock) { + } else if (isBlock || isParentSuperClass) { // Trailing block comments never need a newline return concat([" ", contents]); } @@ -1062,7 +499,12 @@ function printComments(path, print, options, needsSemi) { leadingParts.push(contents); const text = options.originalText; - if (util.hasNewline(text, util.skipNewline(text, util.locEnd(comment)))) { + if ( + privateUtil.hasNewline( + text, + privateUtil.skipNewline(text, options.locEnd(comment)) + ) + ) { leadingParts.push(hardline); } } else if (trailing) { diff --git a/src/main/get-plugin.js b/src/main/get-plugin.js new file mode 100644 index 00000000..17e4b7bd --- /dev/null +++ b/src/main/get-plugin.js @@ -0,0 +1,19 @@ +"use strict"; + +function getPlugin(options) { + const astFormat = options.astFormat; + + if (!astFormat) { + throw new Error("getPlugin() requires astFormat to be set"); + } + const printerPlugin = options.plugins.find( + plugin => plugin.printers[astFormat] + ); + if (!printerPlugin) { + throw new Error(`Couldn't find plugin for AST format "${astFormat}"`); + } + + return printerPlugin; +} + +module.exports = getPlugin; diff --git a/src/main/get-printer.js b/src/main/get-printer.js deleted file mode 100644 index cba1b100..00000000 --- a/src/main/get-printer.js +++ /dev/null @@ -1,21 +0,0 @@ -"use strict"; - -function getPrinter(options) { - const astFormat = options.astFormat; - - if (!astFormat) { - throw new Error("getPrinter() requires astFormat to be set"); - } - const printerPlugin = options.plugins.find( - plugin => plugin.printers[astFormat] - ); - if (!printerPlugin) { - throw new Error( - `Couldn't find printer plugin for AST format "${astFormat}"` - ); - } - - return printerPlugin.printers[astFormat]; -} - -module.exports = getPrinter; diff --git a/src/main/multiparser.js b/src/main/multiparser.js index 3704e658..9ddcf8e8 100644 --- a/src/main/multiparser.js +++ b/src/main/multiparser.js @@ -20,10 +20,14 @@ function textToDoc(text, partialNextOptions, parentOptions) { Object.assign({}, parentOptions, partialNextOptions, { parentParser: parentOptions.parser, originalText: text - }) + }), + { passThrough: true, inferParser: false } ); - const ast = require("./parser").parse(text, nextOptions); + const result = require("./parser").parse(text, nextOptions); + const ast = result.ast; + text = result.text; + const astComments = ast.comments; delete ast.comments; comments.attach(astComments, ast, text, nextOptions); diff --git a/src/main/options-descriptor.js b/src/main/options-descriptor.js new file mode 100644 index 00000000..8dc53ef7 --- /dev/null +++ b/src/main/options-descriptor.js @@ -0,0 +1,22 @@ +"use strict"; + +function apiDescriptor(name, value) { + return arguments.length === 1 + ? JSON.stringify(name) + : `\`{ ${apiDescriptor(name)}: ${JSON.stringify(value)} }\``; +} + +function cliDescriptor(name, value) { + return value === false + ? `\`--no-${name}\`` + : value === true || arguments.length === 1 + ? `\`--${name}\`` + : value === "" + ? `\`--${name}\` without an argument` + : `\`--${name}=${value}\``; +} + +module.exports = { + apiDescriptor, + cliDescriptor +}; diff --git a/src/main/options-normalizer.js b/src/main/options-normalizer.js new file mode 100644 index 00000000..3457a8b1 --- /dev/null +++ b/src/main/options-normalizer.js @@ -0,0 +1,153 @@ +"use strict"; + +const leven = require("leven"); +const validator = require("./options-validator"); +const descriptors = require("./options-descriptor"); + +function normalizeOptions(options, optionInfos, opts) { + opts = opts || {}; + const logger = + opts.logger === false + ? { warn() {} } + : opts.logger !== undefined ? opts.logger : console; + const descriptor = opts.descriptor || descriptors.apiDescriptor; + const passThrough = opts.passThrough || []; + + const optionInfoMap = optionInfos.reduce( + (reduced, optionInfo) => + Object.assign(reduced, { [optionInfo.name]: optionInfo }), + {} + ); + const normalizedOptions = Object.keys(options).reduce((newOptions, key) => { + const optionInfo = optionInfoMap[key]; + + let optionName = key; + let optionValue = options[key]; + + if (!optionInfo) { + if (passThrough === true || passThrough.indexOf(optionName) !== -1) { + newOptions[optionName] = optionValue; + } else { + logger.warn( + createUnknownOptionMessage( + optionName, + optionValue, + optionInfos, + descriptor + ) + ); + } + return newOptions; + } + + if (!optionInfo.deprecated) { + optionValue = normalizeOption(optionValue, optionInfo); + } else if (typeof optionInfo.redirect === "string") { + logger.warn(createRedirectOptionMessage(optionInfo, descriptor)); + optionName = optionInfo.redirect; + } else if (optionValue) { + logger.warn(createRedirectOptionMessage(optionInfo, descriptor)); + optionValue = optionInfo.redirect.value; + optionName = optionInfo.redirect.option; + } + + if (optionInfo.choices) { + const choiceInfo = optionInfo.choices.find( + choice => choice.value === optionValue + ); + if (choiceInfo && choiceInfo.deprecated) { + logger.warn( + createRedirectChoiceMessage(optionInfo, choiceInfo, descriptor) + ); + optionValue = choiceInfo.redirect; + } + } + + if (optionInfo.array && !Array.isArray(optionValue)) { + optionValue = [optionValue]; + } + + if (optionValue !== optionInfo.default) { + validator.validateOption(optionValue, optionInfoMap[optionName], { + descriptor + }); + } + + newOptions[optionName] = optionValue; + return newOptions; + }, {}); + + return normalizedOptions; +} + +function normalizeOption(option, optionInfo) { + return optionInfo.type === "int" ? Number(option) : option; +} + +function createUnknownOptionMessage(key, value, optionInfos, descriptor) { + const messages = [`Ignored unknown option ${descriptor(key, value)}.`]; + + const suggestedOptionInfo = optionInfos.find( + optionInfo => leven(optionInfo.name, key) < 3 + ); + if (suggestedOptionInfo) { + messages.push(`Did you mean ${JSON.stringify(suggestedOptionInfo.name)}?`); + } + + return messages.join(" "); +} + +function createRedirectOptionMessage(optionInfo, descriptor) { + return `${descriptor( + optionInfo.name + )} is deprecated. Prettier now treats it as ${ + typeof optionInfo.redirect === "string" + ? descriptor(optionInfo.redirect) + : descriptor(optionInfo.redirect.option, optionInfo.redirect.value) + }.`; +} + +function createRedirectChoiceMessage(optionInfo, choiceInfo, descriptor) { + return `${descriptor( + optionInfo.name, + choiceInfo.value + )} is deprecated. Prettier now treats it as ${descriptor( + optionInfo.name, + choiceInfo.redirect + )}.`; +} + +function normalizeApiOptions(options, optionInfos, opts) { + return normalizeOptions( + options, + optionInfos, + Object.assign({ descriptor: descriptors.apiDescriptor }, opts) + ); +} + +function normalizeCliOptions(options, optionInfos, opts) { + const args = options["_"] || []; + + const newOptions = normalizeOptions( + Object.keys(options).reduce( + (reduced, key) => + Object.assign( + reduced, + key.length === 1 // omit alias + ? null + : { [key]: options[key] } + ), + {} + ), + optionInfos, + Object.assign({ descriptor: descriptors.cliDescriptor }, opts) + ); + newOptions["_"] = args; + + return newOptions; +} + +module.exports = { + normalizeApiOptions, + normalizeCliOptions +}; diff --git a/src/main/options-validator.js b/src/main/options-validator.js new file mode 100644 index 00000000..7608c4b7 --- /dev/null +++ b/src/main/options-validator.js @@ -0,0 +1,81 @@ +"use strict"; + +const descriptors = require("./options-descriptor"); + +function validateOption(value, optionInfo, opts) { + opts = opts || {}; + const descriptor = opts.descriptor || descriptors.apiDescriptor; + + if ( + typeof optionInfo.exception === "function" && + optionInfo.exception(value) + ) { + return; + } + + try { + validateOptionType(value, optionInfo); + } catch (error) { + throw new Error( + `Invalid \`${descriptor(optionInfo.name)}\` value. ${ + error.message + }, but received \`${JSON.stringify(value)}\`.` + ); + } +} + +function validateOptionType(value, optionInfo) { + if (optionInfo.array) { + if (!Array.isArray(value)) { + throw new Error(`Expected an array`); + } + value.forEach(v => + validateOptionType(v, Object.assign({}, optionInfo, { array: false })) + ); + } else { + switch (optionInfo.type) { + case "int": + validateIntOption(value); + break; + case "boolean": + validateBooleanOption(value); + break; + case "choice": + validateChoiceOption(value, optionInfo.choices); + break; + } + } +} + +function validateBooleanOption(value) { + if (typeof value !== "boolean") { + throw new Error(`Expected a boolean`); + } +} + +function validateIntOption(value) { + if ( + !( + typeof value === "number" && + Math.floor(value) === value && + value >= 0 && + value !== Infinity + ) + ) { + throw new Error(`Expected an integer`); + } +} + +function validateChoiceOption(value, choiceInfos) { + if (!choiceInfos.some(choiceInfo => choiceInfo.value === value)) { + const choices = choiceInfos + .filter(choiceInfo => !choiceInfo.deprecated) + .map(choiceInfo => JSON.stringify(choiceInfo.value)) + .sort(); + const head = choices.slice(0, -2); + const tail = choices.slice(-2); + throw new Error(`Expected ${head.concat(tail.join(" or ")).join(", ")}`); + } +} + +module.exports = { validateOption }; diff --git a/src/main/options.js b/src/main/options.js index c52ceea5..da786dbd 100644 --- a/src/main/options.js +++ b/src/main/options.js @@ -1,139 +1,120 @@ "use strict"; const path = require("path"); - -const validate = require("jest-validate").validate; -const deprecatedConfig = require("./deprecated"); const getSupportInfo = require("../common/support").getSupportInfo; +const normalizer = require("./options-normalizer"); const loadPlugins = require("../common/load-plugins"); const resolveParser = require("./parser").resolveParser; -const getPrinter = require("./get-printer"); +const getPlugin = require("./get-plugin"); -const defaults = { - cursorOffset: -1, - rangeStart: 0, - rangeEnd: Infinity, - useTabs: false, - tabWidth: 2, - printWidth: 80, - singleQuote: false, - trailingComma: "none", - bracketSpacing: true, - jsxBracketSameLine: false, - parser: "babylon", - parentParser: "", - insertPragma: false, - requirePragma: false, - semi: true, - proseWrap: "preserve", - arrowParens: "avoid", - plugins: [], +const hiddenDefaults = { astFormat: "estree", printer: {}, - __inJsTemplate: false + locStart: null, + locEnd: null }; -const exampleConfig = Object.assign({}, defaults, { - filepath: "path/to/Filename", - printWidth: 80, - originalText: "text" -}); - // Copy options and fill in default values. -function normalize(options) { - const normalized = Object.assign({}, options || {}); - const filepath = normalized.filepath; +function normalize(options, opts) { + opts = opts || {}; - normalized.plugins = loadPlugins(normalized); + const rawOptions = Object.assign({}, options); - if ( - filepath && - !normalized.parentParser && - (!normalized.parser || normalized.parser === defaults.parser) - ) { - const extension = path.extname(filepath); - const filename = path.basename(filepath).toLowerCase(); + const plugins = loadPlugins(rawOptions.plugins); + rawOptions.plugins = plugins; - const language = getSupportInfo(null, normalized).languages.find( - language => - typeof language.since === "string" && - (language.extensions.indexOf(extension) > -1 || - (language.filenames && - language.filenames.find(name => name.toLowerCase() === filename))) - ); + const supportOptions = getSupportInfo(null, { + plugins, + pluginsLoaded: true, + showUnreleased: true, + showDeprecated: true + }).options; + const defaults = supportOptions.reduce( + (reduced, optionInfo) => + Object.assign(reduced, { [optionInfo.name]: optionInfo.default }), + Object.assign({}, hiddenDefaults) + ); - if (language) { - normalized.parser = language.parsers[0]; + if (opts.inferParser !== false) { + if ( + rawOptions.filepath && + (!rawOptions.parser || rawOptions.parser === defaults.parser) + ) { + const inferredParser = inferParser( + rawOptions.filepath, + rawOptions.plugins + ); + if (inferredParser) { + rawOptions.parser = inferredParser; + } } } - if (normalized.parser === "json") { - normalized.trailingComma = "none"; - } + const parser = resolveParser( + !rawOptions.parser + ? rawOptions + : // handle deprecated parsers + normalizer.normalizeApiOptions( + rawOptions, + [supportOptions.find(x => x.name === "parser")], + { passThrough: true, logger: false } + ) + ); + rawOptions.astFormat = parser.astFormat; + rawOptions.locEnd = parser.locEnd; + rawOptions.locStart = parser.locStart; - /* istanbul ignore if */ - if (typeof normalized.trailingComma === "boolean") { - // Support a deprecated boolean type for the trailing comma config - // for a few versions. This code can be removed later. - normalized.trailingComma = "es5"; + const plugin = getPlugin(rawOptions); + rawOptions.printer = plugin.printers[rawOptions.astFormat]; - // eslint-disable-next-line no-console - console.warn( - "Warning: `trailingComma` without any argument is deprecated. " + - 'Specify "none", "es5", or "all".' + const pluginDefaults = supportOptions + .filter( + optionInfo => + optionInfo.pluginDefaults && optionInfo.pluginDefaults[plugin.name] + ) + .reduce( + (reduced, optionInfo) => + Object.assign(reduced, { + [optionInfo.name]: optionInfo.pluginDefaults[plugin.name] + }), + {} ); - } - /* istanbul ignore if */ - if (typeof normalized.proseWrap === "boolean") { - normalized.proseWrap = normalized.proseWrap ? "always" : "never"; + const mixedDefaults = Object.assign({}, defaults, pluginDefaults); - // eslint-disable-next-line no-console - console.warn( - "Warning: `proseWrap` with boolean value is deprecated. " + - 'Use "always", "never", or "preserve" instead.' - ); - } - - /* istanbul ignore if */ - if (normalized.parser === "postcss") { - normalized.parser = "css"; - - // eslint-disable-next-line no-console - console.warn( - 'Warning: `parser` with value "postcss" is deprecated. ' + - 'Use "css", "less" or "scss" instead.' - ); - } - - const parserBackup = normalized.parser; - if (typeof normalized.parser === "function") { - // Delete the function from the object to pass validation. - delete normalized.parser; - } - - validate(normalized, { exampleConfig, deprecatedConfig }); - - // Restore the option back to a function; - normalized.parser = parserBackup; - - // For backward compatibility. Deprecated in 0.0.10 - /* istanbul ignore if */ - if ("useFlowParser" in normalized) { - normalized.parser = normalized.useFlowParser ? "flow" : "babylon"; - delete normalized.useFlowParser; - } - - normalized.astFormat = resolveParser(normalized).astFormat; - normalized.printer = getPrinter(normalized); - - Object.keys(defaults).forEach(k => { - if (normalized[k] == null) { - normalized[k] = defaults[k]; + Object.keys(mixedDefaults).forEach(k => { + if (rawOptions[k] == null) { + rawOptions[k] = mixedDefaults[k]; } }); - return normalized; + if (rawOptions.parser === "json") { + rawOptions.trailingComma = "none"; + } + + return normalizer.normalizeApiOptions( + rawOptions, + supportOptions, + Object.assign({ passThrough: Object.keys(hiddenDefaults) }, opts) + ); } -module.exports = { normalize, defaults }; +function inferParser(filepath, plugins) { + const extension = path.extname(filepath); + const filename = path.basename(filepath).toLowerCase(); + + const language = getSupportInfo(null, { + plugins, + pluginsLoaded: true + }).languages.find( + language => + language.since !== null && + (language.extensions.indexOf(extension) > -1 || + (language.filenames && + language.filenames.find(name => name.toLowerCase() === filename))) + ); + + return language && language.parsers[0]; +} + +module.exports = { normalize, hiddenDefaults }; diff --git a/src/main/parser.js b/src/main/parser.js index 6e04267a..8452be29 100644 --- a/src/main/parser.js +++ b/src/main/parser.js @@ -2,6 +2,10 @@ const path = require("path"); const ConfigError = require("../common/errors").ConfigError; +const js = require("../language-js/index.js"); + +const locStart = js.locStart; +const locEnd = js.locEnd; function getParsers(options) { return options.plugins.reduce( @@ -17,7 +21,9 @@ function resolveParser(opts, parsers) { // Custom parser API always works with JavaScript. return { parse: opts.parser, - astFormat: "estree" + astFormat: "estree", + locStart, + locEnd }; } @@ -28,7 +34,9 @@ function resolveParser(opts, parsers) { try { return { parse: eval("require")(path.resolve(process.cwd(), opts.parser)), - astFormat: "estree" + astFormat: "estree", + locStart, + locEnd }; } catch (err) { /* istanbul ignore next */ @@ -58,7 +66,14 @@ function parse(text, opts) { const parser = resolveParser(opts, parsers); try { - return parser.parse(text, parsersForCustomParserApi, opts); + if (parser.preprocess) { + text = parser.preprocess(text, opts); + } + + return { + text, + ast: parser.parse(text, parsersForCustomParserApi, opts) + }; } catch (error) { const loc = error.loc; diff --git a/tests/class_comment/__snapshots__/jsfmt.spec.js.snap b/tests/class_comment/__snapshots__/jsfmt.spec.js.snap index 3216c3ee..ea348067 100644 --- a/tests/class_comment/__snapshots__/jsfmt.spec.js.snap +++ b/tests/class_comment/__snapshots__/jsfmt.spec.js.snap @@ -51,8 +51,8 @@ export class SnapshotLogger { } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ class A // comment 1 -// comment 2 -extends B {} + // comment 2 + extends B {} class A extends B // comment1 // comment2 @@ -66,8 +66,8 @@ class A extends B /* a */ { class A /* a */ extends B {} (class A // comment 1 -// comment 2 -extends B {}); + // comment 2 + extends B {}); (class A extends B // comment1 // comment2 @@ -91,8 +91,8 @@ class X { TEMPLATE = // tab index is needed so we can focus, which is needed for keyboard events '
' + - '
' + - "
"; + '
' + + "
"; } export class SnapshotLogger { diff --git a/tests/classes/__snapshots__/jsfmt.spec.js.snap b/tests/classes/__snapshots__/jsfmt.spec.js.snap index 6c76fff6..9f419ee0 100644 --- a/tests/classes/__snapshots__/jsfmt.spec.js.snap +++ b/tests/classes/__snapshots__/jsfmt.spec.js.snap @@ -50,11 +50,9 @@ export class VisTimelineComponent2 implements AfterViewInit, OnChanges, OnDestroy, AndSomethingReallyReallyLong { } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -class MyContractSelectionWidget extends React.Component< - void, - MyContractSelectionWidgetPropsType, - void -> implements SomethingLarge { +class MyContractSelectionWidget + extends React.Component + implements SomethingLarge { method() {} } @@ -65,7 +63,8 @@ class DisplayObject extends utils.EventEmitter implements interaction_InteractiveTarget {} class DisplayObject extends utils.EventEmitter - implements interaction_InteractiveTarget, + implements + interaction_InteractiveTarget, somethingElse_SomeOtherThing, somethingElseAgain_RunningOutOfNames {} @@ -84,7 +83,8 @@ class Foo extends Immutable.Record({ export class VisTimelineComponent implements AfterViewInit, OnChanges, OnDestroy {} export class VisTimelineComponent2 - implements AfterViewInit, + implements + AfterViewInit, OnChanges, OnDestroy, AndSomethingReallyReallyLong {} @@ -161,6 +161,25 @@ class C { `; +exports[`property.js 1`] = ` +class A { + foobar = + // comment to break + 1 + + // comment to break again + 2; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + foobar = + // comment to break + 1 + + // comment to break again + 2; +} + +`; + exports[`ternary.js 1`] = ` if (1) (class {}) ? 1 : 2; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/classes/property.js b/tests/classes/property.js new file mode 100644 index 00000000..94c428b5 --- /dev/null +++ b/tests/classes/property.js @@ -0,0 +1,7 @@ +class A { + foobar = + // comment to break + 1 + + // comment to break again + 2; +} diff --git a/tests/classes_private_fields/__snapshots__/jsfmt.spec.js.snap b/tests/classes_private_fields/__snapshots__/jsfmt.spec.js.snap index d97244f5..14fd5c91 100644 --- a/tests/classes_private_fields/__snapshots__/jsfmt.spec.js.snap +++ b/tests/classes_private_fields/__snapshots__/jsfmt.spec.js.snap @@ -173,3 +173,41 @@ class Point { } `; + +exports[`with_comments.js 1`] = ` +class A { + #foobar = + // comment to break + 1 + + // comment to break again + 2; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + #foobar = + // comment to break + 1 + + // comment to break again + 2; +} + +`; + +exports[`with_comments.js 2`] = ` +class A { + #foobar = + // comment to break + 1 + + // comment to break again + 2; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class A { + #foobar = + // comment to break + 1 + + // comment to break again + 2 +} + +`; diff --git a/tests/classes_private_fields/with_comments.js b/tests/classes_private_fields/with_comments.js new file mode 100644 index 00000000..31549559 --- /dev/null +++ b/tests/classes_private_fields/with_comments.js @@ -0,0 +1,7 @@ +class A { + #foobar = + // comment to break + 1 + + // comment to break again + 2; +} diff --git a/tests/comments/__snapshots__/jsfmt.spec.js.snap b/tests/comments/__snapshots__/jsfmt.spec.js.snap index cc30ecca..0757ebef 100644 --- a/tests/comments/__snapshots__/jsfmt.spec.js.snap +++ b/tests/comments/__snapshots__/jsfmt.spec.js.snap @@ -82,6 +82,33 @@ exports[`blank.js 1`] = ` `; +exports[`break-continue-statements.js 1`] = ` +for (;;) { + break /* comment */; + continue /* comment */; +} + +loop: for (;;) { + break /* comment */ loop; + break loop /* comment */; + continue /* comment */ loop; + continue loop /* comment */; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +for (;;) { + break; /* comment */ + continue; /* comment */ +} + +loop: for (;;) { + break /* comment */ loop; + break loop /* comment */; + continue /* comment */ loop; + continue loop /* comment */; +} + +`; + exports[`call_comment.js 1`] = ` render( // Warm any cache , @@ -228,9 +255,41 @@ import("something" /* Hello */ + "else"); exports[`export.js 1`] = ` export //comment {} + +export /* comment */ {}; + +export { + foo // comment +} + +export { + // comment + bar +} + +export { + fooo, // comment + barr, // comment +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -//comment -export {}; +export //comment +{}; + +export /* comment */{}; + +export { + foo // comment +}; + +export { + // comment + bar +}; + +export { + fooo, // comment + barr // comment +}; `; @@ -439,20 +498,21 @@ true if (1) { // comment false; -} else if (2) - // comment - true; +} +// comment +else if (2) true; +// multi +// ple +// lines else if (3) - // multi - // ple - // lines // existing comment true; +// okay? else if (4) { - // okay? // empty with existing comment -} else { - // comment +} +// comment +else { } if (5) @@ -465,8 +525,8 @@ if (6) { } else if (7) // comment true; +// comment else { - // comment true; } @@ -478,9 +538,9 @@ if (8) { // comment // comment true; +// comment +// comment else { - // comment - // comment true; } @@ -496,8 +556,7 @@ else if (12) else if (13) /* comment */ /* comment */ // comment true; -else { - /* comment */ +/* comment */ else { true; } @@ -722,8 +781,9 @@ if (1) { // Comments trigger invalid JavaScript in-between else if if (1) { -} else { - // Comment +} +// Comment +else { } // The comment makes the line break in a weird way diff --git a/tests/comments/break-continue-statements.js b/tests/comments/break-continue-statements.js new file mode 100644 index 00000000..ce8f6617 --- /dev/null +++ b/tests/comments/break-continue-statements.js @@ -0,0 +1,11 @@ +for (;;) { + break /* comment */; + continue /* comment */; +} + +loop: for (;;) { + break /* comment */ loop; + break loop /* comment */; + continue /* comment */ loop; + continue loop /* comment */; +} diff --git a/tests/comments/export.js b/tests/comments/export.js index 75a4727a..d357281a 100644 --- a/tests/comments/export.js +++ b/tests/comments/export.js @@ -1,2 +1,18 @@ export //comment {} + +export /* comment */ {}; + +export { + foo // comment +} + +export { + // comment + bar +} + +export { + fooo, // comment + barr, // comment +} diff --git a/tests/css_atrule/__snapshots__/jsfmt.spec.js.snap b/tests/css_atrule/__snapshots__/jsfmt.spec.js.snap index 8230ead9..0bef718a 100644 --- a/tests/css_atrule/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_atrule/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,1863 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`at-root.css 1`] = ` +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { +@at-root { +.child1 { +width: 100px; +} +.child2 { +width: 200px; +} +} +} +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent +{ +@at-root +{ +.child1 +{ +width +: +100px +; +} +.child2 +{ +width +: +200px +; +} +} +} +.parent + +{ + +@at-root + +{ + +.child1 + +{ + +width + +: + +100px + +; + +} +.child2 + +{ + +width + +: + +200px + +; + +} + +} + +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent { +@at-root .child { +width: 100px; +} +} +.parent{ + @at-root .child{ + width: 100px; + } +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent +{ +@at-root +.child +{ +width +: +100px +; +} +} +.parent + +{ + +@at-root + +.child + +{ + +width + +: + +100px + +; + +} + +} +.parent { +@at-root +input[ +type += +'radio' +] +{ +color +: +red +; +} +} +@media print { + .page { + width: 8in; + + @at-root (with: media) { + color: red; + } + } +} +@media print { +.page { +width: 8in; + +@at-root (with: media) { +color: red; +} +} +} +@media print{ + .page{ + width: 8in; + + @at-root (with:media){ + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root ( with : media ) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root ( with : media ) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root + (with: media) { + color: red; + } + } +} +@media print +{ +.page +{ +width +: +8in +; +@at-root +( +with +: +media +) +{ +color +: +red +; +} +} +} +@media print + +{ + +.page + +{ + +width + +: + +8in + +; + +@at-root + +( + +with + +: + +media + +) + +{ + +color +: + +red + +; + +} + +} + +} +@media print { + .page { + width: 8in; + + @at-root (without: media) { + color: red; + } + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent { + @at-root input[type="radio"] { + color: red; + } +} +@media print { + .page { + width: 8in; + + @at-root (with: media) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root (with: media) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root (with: media) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root (with: media) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root (with: media) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root (with: media) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + @at-root (with: media) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root (with: media) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root (without: media) { + color: red; + } + } +} + +`; + +exports[`charset.css 1`] = ` +@charset "UTF-8"; +@charset "iso-8859-15"; +@charset 'iso-8859-15'; /* Invalid, wrong quoting style used */ +@charset "UTF-8"; /* Invalid, more than one space */ +@charset "UTF-8"; /* Invalid, there is a character (a space) before the at-rule */ +@charset UTF-8; /* Invalid, without ' or ", the charset is not a CSS */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@charset "UTF-8"; +@charset "iso-8859-15"; +@charset 'iso-8859-15'; /* Invalid, wrong quoting style used */ +@charset "UTF-8"; /* Invalid, more than one space */ +@charset "UTF-8"; /* Invalid, there is a character (a space) before the at-rule */ +@charset UTF-8; /* Invalid, without ' or ", the charset is not a CSS */ + +`; + +exports[`counter-style.css 1`] = ` +@counter-style identifier { + system: cyclic; + symbols: "\\1F44D"; + suffix: " "; +} +@counter-style identifier { +system: cyclic; +symbols: "\\1F44D"; +suffix: " "; +} +@counter-style identifier{ + system: cyclic; + symbols: "\\1F44D"; + suffix: " "; +} +@counter-style identifier { + system: cyclic; + symbols: "\\1F44D"; + suffix: " "; +} +@counter-style +identifier +{ +system +: +cyclic +; +symbols +: +"\\1F44D" +; +suffix +: +" " +; +} + +@counter-style + +identifier + +{ + +system + +: + +cyclic + +; + +symbols + + +: + +"\\1F44D" + +; + +suffix + +: + +" " + +; + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@counter-style identifier { + system: cyclic; + symbols: "\\1F44D"; + suffix: " "; +} +@counter-style identifier { + system: cyclic; + symbols: "\\1F44D"; + suffix: " "; +} +@counter-style identifier { + system: cyclic; + symbols: "\\1F44D"; + suffix: " "; +} +@counter-style identifier { + system: cyclic; + symbols: "\\1F44D"; + suffix: " "; +} +@counter-style identifier { + system: cyclic; + symbols: "\\1F44D"; + suffix: " "; +} + +@counter-style identifier { + system: cyclic; + + symbols: "\\1F44D"; + + suffix: " "; +} + +`; + +exports[`custom-media.css 1`] = ` +@custom-media --small-viewport (max-width: 30em); +@custom-media --small-viewport (max-width:30em); +@custom-media --small-viewport ( max-width : 30em ) ; +@custom-media --small-viewport + (max-width: 30em); +@custom-media + --small-viewport + ( + max-width: 30em + ); +@custom-media +--small-viewport +( +max-width +: +30em +) +; +@custom-media + +--small-viewport + +( + +max-width + +: + +30em + +) + +; +@custom-media --none not all; +@custom-media --none not all ; +@custom-media --none + not all; +@custom-media --none + not + all; +@custom-media +--none +not +all +; +@custom-media + +--none + +not + +all + +; +@custom-media --tablet (min-width: 768px) and (max-width: 1279px); +@custom-media --tablet (min-width:768px) and (max-width:1279px); +@custom-media --tablet ( min-width : 768px ) and ( max-width : 1279px ) ; +@custom-media --tablet + (min-width: 768px) and (max-width: 1279px); +@custom-media + --tablet + (min-width: 768px) and + (max-width: 1279px); +@custom-media +--tablet +( +min-width +: +768px +) +and +( +max-width +: +1279px +) +; +@custom-media + +--tablet + +( + +min-width + +: + +768px + +) + +and + +( + +max-width + +: + +1279px + +) + +; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@custom-media --small-viewport (max-width: 30em); +@custom-media --small-viewport (max-width: 30em); +@custom-media --small-viewport (max-width: 30em); +@custom-media --small-viewport (max-width: 30em); +@custom-media --small-viewport (max-width: 30em); +@custom-media --small-viewport (max-width: 30em); +@custom-media --small-viewport (max-width: 30em); +@custom-media --none not all; +@custom-media --none not all; +@custom-media --none not all; +@custom-media --none not all; +@custom-media --none not all; +@custom-media --none not all; +@custom-media --tablet (min-width: 768px) and (max-width: 1279px); +@custom-media --tablet (min-width: 768px) and (max-width: 1279px); +@custom-media --tablet (min-width: 768px) and (max-width: 1279px); +@custom-media --tablet (min-width: 768px) and (max-width: 1279px); +@custom-media --tablet (min-width: 768px) and (max-width: 1279px); +@custom-media --tablet (min-width: 768px) and (max-width: 1279px); +@custom-media --tablet (min-width: 768px) and (max-width: 1279px); + +`; + +exports[`custom-selector.css 1`] = ` +@custom-selector :--heading h1, h2, h3, h4, h5, h6; +@custom-selector :--heading h1,h2,h3,h4,h5,h6; +@custom-selector :--heading h1 , h2 , h3 , h4 , h5 , h6 ; +@custom-selector :--heading + h1, h2, h3, h4, h5, h6; +@custom-selector + :--heading + h1, + h2, + h3, + h4, + h5, + h6; +@custom-selector +:--heading +h1 +, +h2 +, +h3 +, +h4 +, +h5 +, +h6 +; +@custom-selector + +:--heading + +h1 + +, + +h2 + +, + +h3 + +, + +h4 + +, + +h5 + +, + +h6 + +; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name h1, h2, h3, h4, h5, h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name h1 + h1, h2, h3, h4, h5, h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name .very-very-very-very-very-very-very-very-very-long-selector-name + very-very-very-very-very-very-very-very-very-long-selector-name-other, h2, h3, h4, h5, h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name h1 h1, h2, h3, h4, h5, h6; +@custom-selector :--heading .very-very-very-very-very-very-very-very-very-very-long-class-name, .very-very-very-very-very-very-very-very-very-very-long-class-name-other; +@custom-selector :--enter :hover, :focus; +@custom-selector :--visible :global.visible; +@custom-selector :--icon i[class^='icon-'], i[class*=' icon-']; +@custom-selector :--placeholder :placehoder-shown, ::-webkit-input-placeholder, ::-moz-placeholder, :-ms-input-placeholder; +@custom-selector :--enter :matches(:hover, :focus, :active); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@custom-selector :--heading h1, h2, h3, h4, h5, h6; +@custom-selector :--heading h1, h2, h3, h4, h5, h6; +@custom-selector :--heading h1, h2, h3, h4, h5, h6; +@custom-selector :--heading h1, h2, h3, h4, h5, h6; +@custom-selector :--heading h1, h2, h3, h4, h5, h6; +@custom-selector :--heading h1, h2, h3, h4, h5, h6; +@custom-selector :--heading h1, h2, h3, h4, h5, h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name + h1, + h2, + h3, + h4, + h5, + h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name + h1 + h1, + h2, + h3, + h4, + h5, + h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name + .very-very-very-very-very-very-very-very-very-long-selector-name + + very-very-very-very-very-very-very-very-very-long-selector-name-other, + h2, + h3, + h4, + h5, + h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name + h1 h1, + h2, + h3, + h4, + h5, + h6; +@custom-selector :--heading + .very-very-very-very-very-very-very-very-very-very-long-class-name, + .very-very-very-very-very-very-very-very-very-very-long-class-name-other; +@custom-selector :--enter :hover, :focus; +@custom-selector :--visible :global.visible; +@custom-selector :--icon i[class^="icon-"], i[class*=" icon-"]; +@custom-selector :--placeholder + :placehoder-shown, + ::-webkit-input-placeholder, + ::-moz-placeholder, + :-ms-input-placeholder; +@custom-selector :--enter :matches(:hover, :focus, :active); + +`; + +exports[`debug.css 1`] = ` +@debug 10em + 12em; +@debug 10em+12em; +@debug 10em + 12em ; +@debug 10em + + + 12em + ; +@debug +10em ++ +12em +; +@debug + +10em + ++ + +12em + +; +@debug $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@debug 10em + 12em; +@debug 10em+12em; +@debug 10em + 12em; +@debug 10em + 12em; +@debug 10em + 12em; +@debug 10em + 12em; +@debug $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var + + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var; + +`; + +exports[`each.css 1`] = ` +@each $animal in puma, sea-slug, egret, salamander {} +@each $animal in puma,sea-slug,egret,salamander{} +@each $animal in puma , sea-slug , egret , salamander {} +@each $animal in + puma, sea-slug, egret, salamander {} +@each + $animal + in + puma + , + sea-slug + , + egret + , + salamander + { + } +@each +$animal +in +puma +, +sea-slug +, +egret +, +salamander +{ +} +@each + +$animal + +in + +puma + +, + +sea-slug + +, + +egret + +, + +salamander + +{ + +} +@each $animal in ((puma), (sea-slug), (egret), (salamander)) {} +@each $animal in((puma),(sea-slug),(egret),(salamander)){} +@each $animal in ( ( puma ) , ( sea-slug ) , ( egret ) , ( salamander ) ) {} +@each $animal + in ((puma), (sea-slug), (egret), (salamander)) {} +@each + $animal + in ( + (puma), + (sea-slug), + (egret), + (salamander) + ) { } +@each + $animal + in + ( + ( + puma + ) + , + ( + sea-slug + ) + , + ( + egret + ) + , + ( + salamander + ) + ) + { + } +@each + + $animal + + in + + ( + + ( + + puma + + ) + + , + + ( + + sea-slug + + ) + + , + + ( + + egret + + ) + + , + + ( + + salamander + + ) + + ) + + { + + } +@each $animal, $color, $cursor in (puma, black, default), (sea-slug, blue, pointer), (egret, white, move) {} +@each $animal,$color,$cursor in (puma,black,default),(sea-slug,blue,pointer),(egret,white,move){} +@each $animal, $color, $cursor in + (puma, black, default), + (sea-slug, blue, pointer), + (egret, white, move) {} +@each $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var in puma, sea-slug, egret, salamander {} +@each $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 in (puma, black, default), (sea-slug, blue, pointer), (egret, white, move) {} +@each $element, $size in (h1: 20px, h2: 16px, h3: 14px) {} +@each $element,$size in(h1:20px,h2:16px,h3:14px){} +@each $element , $size in ( h1 : 20px , h2 : 16px , h3 : 14px ) {} +@each $element, + $size in (h1: 20px, h2: 16px, h3: 14px) {} +@each + $element, + $size + in + ( + h1 + : + 20px + , + h2 + : + 16px + , + h3 + : + 14px + ) + { + } +@each +$element, +$size +in +( +h1 +: +20px +, +h2 +: +16px +, +h3 +: +14px +) +{ +} +@each +$element, + +$size + +in + +( + +h1 + +: + +20px + +, + +h2 + +: + +16px + +, + +h3 + +: + +14px + +) + +{ + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@each $animal in puma, sea-slug, egret, salamander { +} +@each $animal in puma, sea-slug, egret, salamander { +} +@each $animal in puma, sea-slug, egret, salamander { +} +@each $animal in puma, sea-slug, egret, salamander { +} +@each $animal in puma, sea-slug, egret, salamander { +} +@each $animal in puma, sea-slug, egret, salamander { +} +@each $animal in puma, sea-slug, egret, salamander { +} +@each $animal in ((puma), (sea-slug), (egret), (salamander)) { +} +@each $animal in((puma), (sea-slug), (egret), (salamander)) { +} +@each $animal in ((puma), (sea-slug), (egret), (salamander)) { +} +@each $animal in ((puma), (sea-slug), (egret), (salamander)) { +} +@each $animal in ((puma), (sea-slug), (egret), (salamander)) { +} +@each $animal in ((puma), (sea-slug), (egret), (salamander)) { +} +@each $animal in ((puma), (sea-slug), (egret), (salamander)) { +} +@each $animal, $color, $cursor in (puma, black, default), + (sea-slug, blue, pointer), (egret, white, move) +{ +} +@each $animal, $color, $cursor in (puma, black, default), + (sea-slug, blue, pointer), (egret, white, move) +{ +} +@each $animal, $color, $cursor in (puma, black, default), + (sea-slug, blue, pointer), (egret, white, move) +{ +} +@each $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var + in puma, + sea-slug, egret, salamander +{ +} +@each $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 + in (puma, black, default), + (sea-slug, blue, pointer), (egret, white, move) +{ +} +@each $element, $size in (h1: 20px, h2: 16px, h3: 14px) { +} +@each $element, $size in(h1: 20px, h2: 16px, h3: 14px) { +} +@each $element, $size in (h1: 20px, h2: 16px, h3: 14px) { +} +@each $element, $size in (h1: 20px, h2: 16px, h3: 14px) { +} +@each $element, $size in (h1: 20px, h2: 16px, h3: 14px) { +} +@each $element, $size in (h1: 20px, h2: 16px, h3: 14px) { +} +@each $element, $size in (h1: 20px, h2: 16px, h3: 14px) { +} + +`; + +exports[`extend.css 1`] = ` +.master { + color: black; + font-size: 12px; +} +.emphasis { + @extend .master; + font-weight: bold; +} +.emphasis { +@extend .master; +font-weight: bold; +} +.emphasis { + @extend .master ; + font-weight: bold; +} +.emphasis { + @extend .master ; + font-weight: bold; +} +.emphasis +{ +@extend +.master +; +font-weight +: +bold +; +} +.emphasis + +{ + +@extend + +.master + +; + +font-weight + +: + +bold + +; + +} +%message-shared { + border: 1px solid #ccc; + padding: 10px; + color: #333; +} +.message { + @extend %message-shared; +} +.message { +@extend %message-shared; +} +.message { + @extend %message-shared ; +} +.message { + @extend %message-shared ; +} +.message { +@extend +%message-shared; +} +.message { + +@extend + +%message-shared; + +} +a.important { + @extend .notice !optional; +} +a.important { +@extend .notice !optional; +} +a.important { + @extend .notice !optional ; +} +a.important { + @extend .notice !optional ; +} +a.important +{ +@extend +.notice +!optional +; +} +a.important + +{ + +@extend + +.notice + +!optional + +; + +} +.message-error { + @extend .message, .important; +} +.message-error { + @extend .message , .important ; +} +.message-error { + @extend .message , .important ; +} +.message-error { + @extend .message, + .important; +} +.message-error { + @extend + .message, + .important; +} +.message-error +{ +@extend +.message +, +.important +; +} +.message-error + +{ + +@extend + +.message + +, + +.important; + +} +.message-error { + @extend .very-very-very-very-very-very-very-very-very-very-very-very-very-long-selector, .very-very-very-very-very-very-very-very-very-very-very-very-very-long-selector; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.master { + color: black; + font-size: 12px; +} +.emphasis { + @extend .master; + font-weight: bold; +} +.emphasis { + @extend .master; + font-weight: bold; +} +.emphasis { + @extend .master; + font-weight: bold; +} +.emphasis { + @extend .master; + font-weight: bold; +} +.emphasis { + @extend .master; + font-weight: bold; +} +.emphasis { + @extend .master; + + font-weight: bold; +} +%message-shared { + border: 1px solid #ccc; + padding: 10px; + color: #333; +} +.message { + @extend %message-shared; +} +.message { + @extend %message-shared; +} +.message { + @extend %message-shared; +} +.message { + @extend %message-shared; +} +.message { + @extend %message-shared; +} +.message { + @extend %message-shared; +} +a.important { + @extend .notice !optional; +} +a.important { + @extend .notice !optional; +} +a.important { + @extend .notice !optional; +} +a.important { + @extend .notice !optional; +} +a.important { + @extend .notice !optional; +} +a.important { + @extend .notice !optional; +} +.message-error { + @extend .message, .important; +} +.message-error { + @extend .message, .important; +} +.message-error { + @extend .message, .important; +} +.message-error { + @extend .message, .important; +} +.message-error { + @extend .message, .important; +} +.message-error { + @extend .message, .important; +} +.message-error { + @extend .message, .important; +} +.message-error { + @extend .very-very-very-very-very-very-very-very-very-very-very-very-very-long-selector, + .very-very-very-very-very-very-very-very-very-very-very-very-very-long-selector; +} + +`; + +exports[`font-face.css 1`] = ` +@font-face { + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face { +font-family: "Open Sans"; +src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), +url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face{ + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face { + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face +{ +font-family +: +"Open Sans" +; +src +: +url( +"/fonts/OpenSans-Regular-webfont.woff2" +) +format( +"woff2" +) +, +url( +"/fonts/OpenSans-Regular-webfont.woff" +) +format( +"woff" +) +; +} +@font-face + +{ + +font-family + +: + +"Open Sans" + +; + +src + +: + +url( + +"/fonts/OpenSans-Regular-webfont.woff2" + +) + +format( + +"woff2" + +) + +, + +url( + +"/fonts/OpenSans-Regular-webfont.woff" + +) + +format( + + +"woff" + +) + +; + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@font-face { + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face { + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face { + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face { + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face { + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face { + font-family: "Open Sans"; + + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} + +`; + +exports[`font-feature-values.css 1`] = ` +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} +@font-feature-values Font One { +@styleset { +nice-style: 12; +} +} +@font-feature-values Font One{ + @styleset{ + nice-style: 12; + } +} +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} +@font-feature-values +Font One +{ +@styleset +{ +nice-style +: +12 +; +} +} + +@font-feature-values + +Font One + +{ + +@styleset +{ + +nice-style + +: + +12 + +; + +} + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} + +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} + +`; + +exports[`for.css 1`] = ` +@for $i from 1 through 8 {} +@for $i from 1 through 8{} +@for $i from 1 through 8 {} +@for $i +from +1 +through +8 +{} +@for +$i +from +1 +through +8 +{} +@for +$i +from +1 +through +8 +{} +@for + +$i + +from + +1 + +through + +8 + +{} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from 1 through 5 {} +@for $i from $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 through 5 {} +@for $i from 1 through $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 {} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 through $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 {} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 end $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 {} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from ($var1 + $var1) through ($var-2 + $var-2) {} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from ($very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1) through ($very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2) {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@for $i from 1 through 8 { +} +@for $i from 1 through 8 { +} +@for $i from 1 through 8 { +} +@for $i from 1 through 8 { +} +@for $i from 1 through 8 { +} +@for $i from 1 through 8 { +} +@for $i from 1 through 8 { +} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var + from 1 + through 5 +{ +} +@for $i + from $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 + through 5 +{ +} +@for $i + from 1 + through $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 +{ +} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var + from $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 + through $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 +{ +} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var + from $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 + end $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 +{ +} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var + from ($var1 + $var1) + through ($var-2 + $var-2) +{ +} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var + from ( + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 + + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 + ) + through ( + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 + + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 + ) +{ +} + +`; + +exports[`function.css 1`] = ` +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func($arg,$arg1,$arg2:10,$args...){ + @return "Func"; +} +@function func ( $arg , $arg1 , $arg2 : 10 , $args ... ) { + @return "Func"; +} +@function func ( $arg , $arg1 , $arg2 : 10 , $args ... ) { + @return "Func"; +} +@function func( + $arg, + $arg1, + $arg2: 10, + $args... +) { + @return "Func"; +} +@function + func( + $arg, + $arg1, + $arg2: 10, + $args... + ) { + @return "Func"; +} +@function +func +( +$arg +, +$arg1 +, +$arg2 +: +10 +, +$args +... +) +{ +@return +"Func" +; +} +@function + +func + +( + +$arg + +, + +$arg1 + +, + +$arg2 + +: + +10 + +, + +$args + +... + +) + +{ + +@return + +"Func" + +; + +} +@function func($very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: 10, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-args...) { + @return "Func"; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func( + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: + 10, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-args... +) { + @return "Func"; +} + +`; + exports[`if-else.css 1`] = ` @if $media == phonePortrait { $k: .15625; @@ -8,6 +1866,144 @@ exports[`if-else.css 1`] = ` } @else if $media == tabletPortrait { $k: .065106; } +p { + @if $type == ocean {} + @if $type==ocean{} + @if $type == ocean {} + @if $type + == ocean {} + @if + $type + == + ocean + { + } + @if + + $type + + == + + ocean + + { + + } + @if (($type) == (ocean)) {} + @if (($type)==(ocean)){} + @if ( ( $type ) == ( ocean ) ) {} + @if (($type) + == (ocean)) {} + @if + ( + ( + $type + ) + == + ( + ocean + ) + ) + { + } + @if + + ( + + ( + + $type + + ) + + == + + ( + + ocean + + ) + + ) + + { + + } + @if $type == ocean { + color: blue; + } @else if $type == matador { + color: red; + } @else if $type == monster { + color: green; + } @else { + color: black; + } + @if $type == ocean { + } @else if $type == matador { + } + @if $type==ocean{}@else if $type==matador{} + @if $type == ocean { } @else if $type == matador { } + @if $type == ocean {} + @else if $type == matador {} + @if + $type == ocean {} + @else if + $type == matador {} + @if + $type + == + ocean + { + } + @else + if + $type + == + matador + { + } + @if + + $type + + == + + ocean + + { + + } + + @else + + if + + $type + + == + + matador + + { + + } + @if $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables == $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables { + color: blue; + } @else if $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables == $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables { + color: red; + } @else if $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables == $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables { + color: green; + } @else { + color: black; + } + @if $very-very-very-long-var == 0 and $very-very-very-long-var == 0 {} + @if $very-very-very-very-very-very-long-var == 0 and $very-very-very-long-var == 0 {} + @if $very-very-very-very-very-very-very-very-very-very-very-long-var == 0 and $very-very-very-very-very-very-very-very-very-very-very-long-var == 0 {} + @if $base-font-size != 16px or $base-line-height != 24px or $base-unit != 'em' or $h1-font-size != 2 * $base-font-size or $h2-font-size != 1.5 * $base-font-size or $h3-font-size != 1.17 * $base-font-size or $h4-font-size != 1 * $base-font-size or $h5-font-size != 0.83 * $base-font-size or $h6-font-size != 0.67 * $base-font-size or $indent-amount != 40px {} + @if (str-slice($item, 0, 1) == ":") {} + @if (str-slice($item, 0, 3) == " : ") {} + @if ($type == ocean) {} @else if ($type == matador) {} @else {} +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @if $media == phonePortrait { $k: 0.15625; @@ -16,5 +2012,2361 @@ exports[`if-else.css 1`] = ` } @else if $media == tabletPortrait { $k: 0.065106; } +p { + @if $type == ocean { + } + @if $type==ocean { + } + @if $type == ocean { + } + @if $type == ocean { + } + @if $type == ocean { + } + @if $type == ocean { + } + @if (($type) == (ocean)) { + } + @if (($type) ==(ocean)) { + } + @if (($type) == (ocean)) { + } + @if (($type) == (ocean)) { + } + @if (($type) == (ocean)) { + } + @if (($type) == (ocean)) { + } + @if $type == ocean { + color: blue; + } @else if $type == matador { + color: red; + } @else if $type == monster { + color: green; + } @else { + color: black; + } + @if $type == ocean { + } @else if $type == matador { + } + @if $type==ocean { + } @else if $type==matador { + } + @if $type == ocean { + } @else if $type == matador { + } + @if $type == ocean { + } @else if $type == matador { + } + @if $type == ocean { + } @else if $type == matador { + } + @if $type == ocean { + } @else if $type == matador { + } + @if $type == ocean { + } @else if $type == matador { + } + @if $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables == + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables + { + color: blue; + } @else if + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables == + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables + { + color: red; + } @else if + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables == + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables + { + color: green; + } @else { + color: black; + } + @if $very-very-very-long-var == 0 and $very-very-very-long-var == 0 { + } + @if $very-very-very-very-very-very-long-var == + 0 and + $very-very-very-long-var == + 0 + { + } + @if $very-very-very-very-very-very-very-very-very-very-very-long-var == + 0 and + $very-very-very-very-very-very-very-very-very-very-very-long-var == + 0 + { + } + @if $base-font-size != + 16px or + $base-line-height != + 24px or + $base-unit != + "em" or + $h1-font-size != + 2 * + $base-font-size or + $h2-font-size != + 1.5 * + $base-font-size or + $h3-font-size != + 1.17 * + $base-font-size or + $h4-font-size != + 1 * + $base-font-size or + $h5-font-size != + 0.83 * + $base-font-size or + $h6-font-size != + 0.67 * + $base-font-size or + $indent-amount != + 40px + { + } + @if (str-slice($item, 0, 1) == ":") { + } + @if (str-slice($item, 0, 3) == " : ") { + } + @if ($type == ocean) { + } @else if ($type == matador) { + } @else { + } +} + +`; + +exports[`import.css 1`] = ` +@import url("fineprint.css") print; +@import url("bluish.css") projection, tv; +@import 'custom.css'; +@import url("chrome://communicator/skin/"); +@import "common.css" screen, projection; +@import url('landscape.css') screen and (orientation:landscape); + +@import (multiple) "foo.less"; + +/* This isn't valid CSS, SCSS or Less, but we should be lenient and make sure +/* that nothing is lost when printing. */ +@import "one" two "three"; + +#main { + @import "example"; +} +#main { +@import "example"; +} +#main { + @import "example" ; +} +#main { + @import "example" ; +} +#main { + @import + "example"; +} +#main +{ +@import +"example"; +} +#main + +{ + +@import + +"example"; + +} + +@import "test.less" {} +@import "test.less" { + a: b; + c: d; +} + +@import url('foo'); +$dir: 'fonts'; + +@import url("foo.css"); +@import url('foo.css'); +@import url("foo.css"); +@import url("foo.css"); +@import url("fineprint.css")print; +@import url("fineprint.css") print; +@import url("fineprint.css") print; +@import url("fineprint.css") + print; +@import url("bluish.css") projection,tv; +@import url("bluish.css") projection, tv; +@import url("bluish.css") projection ,tv; +@import url("bluish.css") projection , tv; +@import url("bluish.css") projection , tv; +@import url("bluish.css") + projection, + tv; +@import url("very-very-very-very-very-very-very-very-very-very-long-name.css") projection,tv; +@import url('landscape.css') screen and (orientation:landscape); +@import "rounded-corners", "text-shadow"; +@import 'rounded-corners', 'text-shadow'; +$family: unquote("Droid+Sans"); +@import url("http://fonts.googleapis.com/css?family=#{$family}"); +@import url( "foo.css"); +@import url("foo.css" ); +@import url( "foo.css" ); +@import url( "foo.css" ); +@import url("foo bar baz.css"); +@import url( "foo bar baz.css" ); +@import url("foo bar baz.css"); +@import url( +"foo.css" +); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@import url("fineprint.css") print; +@import url("bluish.css") projection, tv; +@import "custom.css"; +@import url("chrome://communicator/skin/"); +@import "common.css" screen, projection; +@import url("landscape.css") screen and (orientation: landscape); + +@import (multiple) "foo.less"; + +/* This isn't valid CSS, SCSS or Less, but we should be lenient and make sure +/* that nothing is lost when printing. */ +@import "one" two "three"; + +#main { + @import "example"; +} +#main { + @import "example"; +} +#main { + @import "example"; +} +#main { + @import "example"; +} +#main { + @import "example"; +} +#main { + @import "example"; +} +#main { + @import "example"; +} + +@import "test.less" { +} +@import "test.less" { + a: b; + c: d; +} + +@import url("foo"); +$dir: "fonts"; + +@import url("foo.css"); +@import url("foo.css"); +@import url("foo.css"); +@import url("foo.css"); +@import url("fineprint.css") print; +@import url("fineprint.css") print; +@import url("fineprint.css") print; +@import url("fineprint.css") print; +@import url("bluish.css") projection, tv; +@import url("bluish.css") projection, tv; +@import url("bluish.css") projection, tv; +@import url("bluish.css") projection, tv; +@import url("bluish.css") projection, tv; +@import url("bluish.css") projection, tv; +@import url("very-very-very-very-very-very-very-very-very-very-long-name.css") + projection, + tv; +@import url("landscape.css") screen and (orientation: landscape); +@import "rounded-corners", "text-shadow"; +@import "rounded-corners", "text-shadow"; +$family: unquote("Droid+Sans"); +@import url("http://fonts.googleapis.com/css?family=#{$family}"); +@import url("foo.css"); +@import url("foo.css"); +@import url("foo.css"); +@import url("foo.css"); +@import url("foo bar baz.css"); +@import url("foo bar baz.css"); +@import url("foo bar baz.css"); +@import url("foo.css"); + +`; + +exports[`include.css 1`] = ` +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix(1px,2px,$arg2:10,2px 4px 6px); +@include mix ( 1px , 2px , $arg2 : 10 , 2px 4px 6px ); +@include mix ( 1px , 2px , $arg2 : 10 , 2px 4px 6px ); +@include mix( + 1px, + 2px, + $arg2: 10, + 2px 4px 6px +); +@include +mix( + 1px, + 2px, + $arg2: 10, + 2px 4px 6px +); +@include +mix +( +1px +, +2px +, +$arg2 +: +10 +, +2px +4px +6px +) +; +@include + +mix + +( + +1px + +, + +2px + +, + +$arg2 + +: + +10 + +, + +2px + +4px + +6px + +) + +; +@include mix($very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg: 1px, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1: 2px, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: 10, 2px 4px 6px); +a { + @include global-variable-overriding; + @include mix($very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg: 1px, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1: 2px, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: 10, 2px 4px 6px); + @include apply-to-ie6-only {} + @include apply-to-ie6-only{} + @include apply-to-ie6-only + {} + @include + apply-to-ie6-only + { + } + @include + + apply-to-ie6-only + + { + + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix( + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg: + 1px, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1: + 2px, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: + 10, + 2px 4px 6px +); +a { + @include global-variable-overriding; + @include mix( + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg: + 1px, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1: + 2px, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: + 10, + 2px 4px 6px + ); + @include apply-to-ie6-only { + } + @include apply-to-ie6-only { + } + @include apply-to-ie6-only { + } + @include apply-to-ie6-only { + } + @include apply-to-ie6-only { + } +} + +`; + +exports[`keyframes.css 1`] = ` +@keyframes identifier { + 0% { + top: 0; + left: 0; + } + 30% { + top: 50px; + } + 68%, + 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes identifier { +0%{top:0;left:0;} +30%{top: 50px;} +68%,72%{left: 50px;} +100%{top: 100px; left: 100%;} +} +@keyframes identifier{ + 0% { + top:0; + left:0; + } + 30% { + top: 50px; + } + 68%, 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes identifier { + 0% { + top:0; + left:0; + } + 30% { + top: 50px; + } + 68%, 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes +identifier +{ +0% +{ +top +: +0; +left +: +0 +; +} +30% +{ +top +: +50px +; +} +68% +, +72% +{ +left +: +50px +; +} +100% +{ +top +: +100px +; +left +: +100% +; +} +} +@keyframes + +identifier + +{ + +0% + +{ + +top + +: + +0 + +; + +left + +: + +0 + +; + +} + +30% + +{ + +top + +: + +50px + +; + +} + +68% + +, + +72% + +{ + +left + +: + +50px + +; + +} + +100% + +{ + +top + +: + +100px + +; + +left + +: + +100% + +; + +} + +} +@keyframes identifier { + from { + margin-top: 50px; + } + to { + margin-top: 100px; + } +} +@keyframes +identifier +{ +from +{ +margin-top: 50px; +} +to +{ +margin-top: 100px; +} +} +@keyframes + +identifier + +{ + +from + +{ + +margin-top: 50px; + +} + +to + +{ + +margin-top: 100px; + +} + +} +@-webkit-keyframes identifier { + 0% { opacity: 0; top: 4rem; } + 100% { opacity: 1; top: 0; } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@keyframes identifier { + 0% { + top: 0; + left: 0; + } + 30% { + top: 50px; + } + 68%, + 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes identifier { + 0% { + top: 0; + left: 0; + } + 30% { + top: 50px; + } + 68%, + 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes identifier { + 0% { + top: 0; + left: 0; + } + 30% { + top: 50px; + } + 68%, + 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes identifier { + 0% { + top: 0; + left: 0; + } + 30% { + top: 50px; + } + 68%, + 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes identifier { + 0% { + top: 0; + left: 0; + } + 30% { + top: 50px; + } + 68%, + 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes identifier { + 0% { + top: 0; + + left: 0; + } + + 30% { + top: 50px; + } + + 68%, + 72% { + left: 50px; + } + + 100% { + top: 100px; + + left: 100%; + } +} +@keyframes identifier { + from { + margin-top: 50px; + } + to { + margin-top: 100px; + } +} +@keyframes identifier { + from { + margin-top: 50px; + } + to { + margin-top: 100px; + } +} +@keyframes identifier { + from { + margin-top: 50px; + } + + to { + margin-top: 100px; + } +} +@-webkit-keyframes identifier { + 0% { + opacity: 0; + top: 4rem; + } + 100% { + opacity: 1; + top: 0; + } +} + +`; + +exports[`media.css 1`] = ` +@media screen and (min-width: 480px) {} +@media screen and (min-width: 480px) {} +@MEDIA screen and (min-width: 480px) {} +@MEDIA screen and (min-width: 480px) {} +@media screen and (min-width: 480px) {} +@media screen and (min-width: 480px) {} +@media screen and (min-width :480px) {} +@media screen and (min-width : 480px) {} +@media screen and (min-width : 480px) {} +@media + screen + and + (min-width + : + 480px) {} +@media screen and ( min-width: 480px) {} +@media screen and (min-width: 480px ) {} +@media screen and ( min-width: 480px ) {} +@media screen and ( min-width: 480px ) {} +@media not screen and (color), print and (color) { } +@media not screen and (color) , print and (color) { } +@media not screen and (color),print and (color) { } +@media not screen and (color) , print and (color) { } +@media not screen and (color) , print and (color) { } +@media not screen and ( color), print and (color) { } +@media not screen and (color ), print and (color) { } +@media not screen and ( color ), print and (color) { } +@media not screen and ( color ), print and (color) { } +@media (--small-viewport) {} +@media +( +--small-viewport +) +{ +} +@media + +( + +--small-viewport + +) + +{ + +} + +.screen-color { + @media screen { + color: green; + + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} +.screen-color { +@media screen { +color: green; + +@media (min-width: 768px) { +color: red; +} +} +@media tv { +color: black; +} +} +.screen-color{ + @media screen{ + color: green; + + @media (min-width: 768px){ + color: red; + } + } + @media tv{ + color: black; + } +} +.screen-color { + @media screen { + color: green; + + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} +.screen-color +{ +@media +screen +{ +color +: +green +; +@media +( +min-width +: +768px +) +{ +color +: +red +; +} +} +@media +tv +{ +color +: +black +; +} +} +.screen-color + +{ + +@media + +screen + +{ + +color + +: + +green + +; + +@media + +( + +min-width + +: + +768px + +) + +{ + +color + +: + +red + +; + +} + +} + +@media + +tv + +{ + +color + +: + +black + +; + +} + +} + +@media all and (-webkit-min-device-pixel-ratio: 1.5), all and (-o-min-device-pixel-ratio: 3/2), all and (min--moz-device-pixel-ratio: 1.5), all and (min-device-pixel-ratio: 1.5) {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media screen and (min-width: 480px) { +} +@media not screen and (color), print and (color) { +} +@media not screen and (color), print and (color) { +} +@media not screen and (color), print and (color) { +} +@media not screen and (color), print and (color) { +} +@media not screen and (color), print and (color) { +} +@media not screen and (color), print and (color) { +} +@media not screen and (color), print and (color) { +} +@media not screen and (color), print and (color) { +} +@media not screen and (color), print and (color) { +} +@media (--small-viewport) { +} +@media (--small-viewport) { +} +@media (--small-viewport) { +} + +.screen-color { + @media screen { + color: green; + + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} +.screen-color { + @media screen { + color: green; + + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} +.screen-color { + @media screen { + color: green; + + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} +.screen-color { + @media screen { + color: green; + + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} +.screen-color { + @media screen { + color: green; + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} +.screen-color { + @media screen { + color: green; + + @media (min-width: 768px) { + color: red; + } + } + + @media tv { + color: black; + } +} + +@media all and (-webkit-min-device-pixel-ratio: 1.5), + all and (-o-min-device-pixel-ratio: 3/2), + all and (min--moz-device-pixel-ratio: 1.5), + all and (min-device-pixel-ratio: 1.5) { +} + +`; + +exports[`mixin.css 1`] = ` +@mixin clearfix {} +@mixin clearfix{} +@mixin clearfix {} +@mixin clearfix + {} +@mixin + clearfix + {} +@mixin +clearfix +{ +} +@mixin + +clearfix + +{ + +} + +@mixin mix($arg, $arg1, $arg2: 10, $args...) {} +@mixin mix($arg,$arg1,$arg2:10,$args...){} +@mixin mix ( $arg , $arg1 , $arg2 : 10 , $args ... ) {} +@mixin mix ( $arg , $arg1 , $arg2 : 10 , $args ... ) {} +@mixin mix( + $arg, + $arg1, + $arg2: 10, + $args... +) {} +@mixin +mix( + $arg, + $arg1, + $arg2: 10, + $args... + ) {} +@mixin +mix +( +$arg +, +$arg1 +, +$arg2 +: +10 +, +$args +... +) +{ +} +@mixin + +mix + +( + +$arg + +, + +$arg1 + +, + +$arg2 + +: + +10 + +, + +$args + +... + +) + +{ +} +@mixin mix($very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: 10, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-args...) {} +@mixin component($conf: ()) {} +@mixin component($conf: ( )) {} +@mixin button-variant($hover-background: darken($background, 7.5%)) {} +@mixin button-variant( $hover-background : darken( $background , 7.5% ) ) {} +@mixin button-variant($hover-background:darken($background,7.5%)) {} +@mixin button-variant( + $hover-background: darken($background, 7.5%) +) {} +@mixin +button-variant( +$hover-background +: +darken( +$background +, +7.5% +) +) +{} +@mixin button-variant($foo: "...") {} +@mixin button-variant($foo: " ... ") {} +@mixin button-variant($foo: " ... ") {} +@mixin sexy-border($color, $width, $foo: (color: red)) {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@mixin clearfix { +} +@mixin clearfix { +} +@mixin clearfix { +} +@mixin clearfix { +} +@mixin clearfix { +} +@mixin clearfix { +} +@mixin clearfix { +} + +@mixin mix($arg, $arg1, $arg2: 10, $args...) { +} +@mixin mix($arg, $arg1, $arg2: 10, $args...) { +} +@mixin mix($arg, $arg1, $arg2: 10, $args...) { +} +@mixin mix($arg, $arg1, $arg2: 10, $args...) { +} +@mixin mix($arg, $arg1, $arg2: 10, $args...) { +} +@mixin mix($arg, $arg1, $arg2: 10, $args...) { +} +@mixin mix($arg, $arg1, $arg2: 10, $args...) { +} +@mixin mix($arg, $arg1, $arg2: 10, $args...) { +} +@mixin mix( + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: + 10, + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-args... +) { +} +@mixin component($conf: ()) { +} +@mixin component($conf: ()) { +} +@mixin button-variant($hover-background: darken($background, 7.5%)) { +} +@mixin button-variant($hover-background: darken($background, 7.5%)) { +} +@mixin button-variant($hover-background: darken($background, 7.5%)) { +} +@mixin button-variant($hover-background: darken($background, 7.5%)) { +} +@mixin button-variant($hover-background: darken($background, 7.5%)) { +} +@mixin button-variant($foo: "...") { +} +@mixin button-variant($foo: " ... ") { +} +@mixin button-variant($foo: " ... ") { +} +@mixin sexy-border($color, $width, $foo: (color: red)) { +} + +`; + +exports[`namespaces.css 1`] = ` +@namespace toto "http://toto.example.org"; +@namespace toto url("http://toto.example.org"); +@namespace toto "http://toto.example.org" ; +@namespace toto "http://toto.example.org" ; +@namespace +toto +"http://toto.example.org" +; +@namespace + +toto + +"http://toto.example.org" + +; +@namespace very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-namespace "http://toto.example.org"; +@namespace +very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-namespace +"http://toto.example.org" +; +@namespace + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-namespace + +"http://toto.example.org" + +; +@namespace "http://example.com/foo"; +@namespace url("http://example.com/foo"); +@namespace url("http://example.com/foo") ; +@namespace url("http://example.com/foo") ; +@namespace +url("http://example.com/foo"); +@namespace + +url("http://example.com/foo"); +@namespace url("http://example.com/foo/very/very/very/very/very/very/very/very/very/very/very/very/very/very"); +@namespace +url("http://example.com/foo/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very"); +@namespace + +url("http://example.com/foo/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very"); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@namespace toto "http://toto.example.org"; +@namespace toto url("http://toto.example.org"); +@namespace toto "http://toto.example.org"; +@namespace toto "http://toto.example.org"; +@namespace toto "http://toto.example.org"; +@namespace toto "http://toto.example.org"; +@namespace very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-namespace "http://toto.example.org"; +@namespace very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-namespace "http://toto.example.org"; +@namespace very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-namespace "http://toto.example.org"; +@namespace "http://example.com/foo"; +@namespace url("http://example.com/foo"); +@namespace url("http://example.com/foo"); +@namespace url("http://example.com/foo"); +@namespace url("http://example.com/foo"); +@namespace url("http://example.com/foo"); +@namespace url("http://example.com/foo/very/very/very/very/very/very/very/very/very/very/very/very/very/very"); +@namespace url("http://example.com/foo/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very"); +@namespace url("http://example.com/foo/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very"); + +`; + +exports[`page.css 1`] = ` +@page { + margin: 1cm; +} +@page { +margin: 1cm; +} +@page{ +margin: 1cm; +} +@page { + margin: 1cm; +} +@page +{ +margin +: +1cm +; +} +@page + +{ + +margin +: + +1cm + +; + +} +@page :first { + margin: 2cm; +} +@page :first { +margin: 2cm; +} +@page :first{ + margin: 2cm; +} +@page :first { + margin: 2cm; +} +@page +:first +{ +margin +: +2cm +; +} +@page + +:first + +{ + +margin + +: + +2cm + +; + +} +@page vertical { + size: A4 portrait; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@page { + margin: 1cm; +} +@page { + margin: 1cm; +} +@page { + margin: 1cm; +} +@page { + margin: 1cm; +} +@page { + margin: 1cm; +} +@page { + margin: 1cm; +} +@page :first { + margin: 2cm; +} +@page :first { + margin: 2cm; +} +@page :first { + margin: 2cm; +} +@page :first { + margin: 2cm; +} +@page :first { + margin: 2cm; +} +@page :first { + margin: 2cm; +} +@page vertical { + size: A4 portrait; +} + +`; + +exports[`return.css 1`] = ` +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { +@return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { +@return $n*$grid-width+($n-1)*$gutter-width/10; +} +@function grid-width($n) { + @return $n * $grid-width + ( $n - 1 ) * $gutter-width / 10 ; +} +@function grid-width($n) { + @return $n * $grid-width + ( $n - 1 ) * $gutter-width / 10 ; +} +@function grid-width($n) { + @return $n + * + $grid-width + + ( + $n + - + 1 + ) + * + $gutter-width + / + 10 + ; +} +@function grid-width($n) { + @return + $n + * + $grid-width + + ( + $n + - + 1 + ) + * + $gutter-width + / + 10 + ; +} +@function +grid-width( +$n +) +{ +@return +$n +* +$grid-width ++ +( +$n +- +1 +) +* +$gutter-width +/ +10 +; +} +@function + +grid-width( + +$n + +) + +{ + +@return + +$n + +* + +$grid-width + ++ + +( + +$n + +- + +1 + +) + +* + +$gutter-width + +/ + +10 + +; + +} +@function grid-width($n) { + @return $very-very-very-very-very-very-vey-long-var * $very-very-very-very-very-very-vey-long-var + ($very-very-very-very-very-very-vey-long-var - 1) * $very-very-very-very-very-very-vey-long-var; +} +@function extend($obj, $ext-obj) { + @return map-merge($obj, $ext-obj); +} +@function extend($obj, $ext-obj) { + @return map-merge( $obj , $ext-obj ) ; +} +@function extend($obj, $ext-obj) { + @return map-merge($obj,$ext-obj); +} +@function extend($obj, $ext-obj) { + @return + map-merge( + $obj + , + $ext-obj + ) + ; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { + @return $n * $grid-width + ($n-1) * $gutter-width/10; +} +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { + @return $very-very-very-very-very-very-vey-long-var * + $very-very-very-very-very-very-vey-long-var + + ($very-very-very-very-very-very-vey-long-var - 1) * + $very-very-very-very-very-very-vey-long-var; +} +@function extend($obj, $ext-obj) { + @return map-merge($obj, $ext-obj); +} +@function extend($obj, $ext-obj) { + @return map-merge($obj, $ext-obj); +} +@function extend($obj, $ext-obj) { + @return map-merge($obj, $ext-obj); +} +@function extend($obj, $ext-obj) { + @return map-merge($obj, $ext-obj); +} + +`; + +exports[`supports.css 1`] = ` +@supports (transform-origin: 5% 5%) {} +@supports (transform-origin:5% 5%) {} +@supports ( transform-origin : 5% 5% ) {} +@supports ( transform-origin : 5% 5% ) {} +@supports + (transform-origin: 5% 5%) {} +@supports +(transform-origin: 5% 5%) {} +@supports +( +transform-origin +: +5% +5% +) {} +@supports + +( + +transform-origin + +: + +5% + +5% + +) + +{} +@supports (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports + (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports +( +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop +: +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value +) {} +@supports + +( + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop + +: + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value + +) + +{} +@supports not(transform-origin: 10em 10em 10em) {} +@supports not (transform-origin: 10em 10em 10em) {} +@supports not ( transform-origin : 10em 10em 10em ) {} +@supports not ( transform-origin : 10em 10em 10em ) {} +@supports not + (transform-origin: 10em 10em 10em) {} +@supports + not (transform-origin: 10em 10em 10em) {} +@supports + not + (transform-origin: 10em 10em 10em) {} +@supports +not +( +transform-origin +: +10em +10em +10em +) {} +@supports + +not + +( + +transform-origin + +: + +10em + +10em + +10em + +) + +{} +@supports not (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports + not (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports not + (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports +not +( +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop +: +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value +) {} +@supports + +not + +( + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop + +: + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value + +) + +{} +@supports not (not (transform-origin: 2px)) {} +@supports not(not(transform-origin:2px)) {} +@supports not ( not ( transform-origin : 2px ) ) {} +@supports not ( not ( transform-origin : 2px ) ) {} +@supports not + (not (transform-origin: 2px)) {} +@supports not + ( + not (transform-origin: 2px) + ) {} +@supports +not +( +not +( +transform-origin +: +2px +) +) {} +@supports + +not + +( + +not + +( + +transform-origin + +: + +2px + +) + +) + +{} +@supports (display: flexbox) and (not (display: inline-grid)) {} +@supports (display: table-cell) and (display: list-item) {} +@supports ( display : table-cell ) and ( display : list-item ) {} +@supports ( display : table-cell ) and ( display : list-item ) {} +@supports + (display: table-cell) and (display: list-item) {} +@supports (display: table-cell) and + (display: list-item) {} +@supports (display: table-cell) + and (display: list-item) {} +@supports +( +display +: +table-cell +) +and +( +display +: +list-item +) {} +@supports + +( + +display + +: + +table-cell + +) + +and + +( + +display + +: + +list-item + +) + +{} +@supports (display: table-cell) and (display: list-item) and (display: run-in) {} +@supports (display: table-cell) and ((display: list-item) and (display: run-in)) {} +@supports + (display: table-cell) and ((display: list-item) and (display: run-in)) {} +@supports (display: table-cell) + and ((display: list-item) + and (display: run-in)) {} +@supports (display: table-cell) and + ((display: list-item) and + (display: run-in)) {} +@supports +( +display +: +table-cell +) +and +( +( +display +: +list-item +) +and +( +display +: +run-in +) +) {} +@supports + +( + +display + +: + +table-cell + +) + +and + +( + +( + +display + +: + +list-item + +) + +and + +( + +display + +: + +run-in + +) + +) + +{} +@supports (transform-style: preserve) or (-moz-transform-style: preserve) {} +@supports (transform-style: preserve) or (-moz-transform-style: preserve) or (-o-transform-style: preserve) or (-webkit-transform-style: preserve) {} +@supports (transform-style: preserve-3d) or ((-moz-transform-style: preserve-3d) or ((-o-transform-style: preserve-3d) or (-webkit-transform-style: preserve-3d))) {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@supports (transform-origin: 5% 5%) { +} +@supports (transform-origin: 5% 5%) { +} +@supports (transform-origin: 5% 5%) { +} +@supports (transform-origin: 5% 5%) { +} +@supports (transform-origin: 5% 5%) { +} +@supports (transform-origin: 5% 5%) { +} +@supports (transform-origin: 5% +5%) { +} +@supports (transform-origin: 5% + +5%) { +} +@supports (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) { +} +@supports (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) { +} +@supports (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) { +} +@supports (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) { +} +@supports not(transform-origin: 10em 10em 10em) { +} +@supports not (transform-origin: 10em 10em 10em) { +} +@supports not (transform-origin: 10em 10em 10em) { +} +@supports not (transform-origin: 10em 10em 10em) { +} +@supports not (transform-origin: 10em 10em 10em) { +} +@supports not (transform-origin: 10em 10em 10em) { +} +@supports not (transform-origin: 10em 10em 10em) { +} +@supports not (transform-origin: 10em +10em +10em) { +} +@supports not (transform-origin: 10em + +10em + +10em) { +} +@supports not (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) { +} +@supports not (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) { +} +@supports not (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) { +} +@supports not (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) { +} +@supports not (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) { +} +@supports not (not (transform-origin: 2px)) { +} +@supports not(not(transform-origin:2px)) { +} +@supports not (not ( transform-origin: 2px )) { +} +@supports not (not ( transform-origin: 2px )) { +} +@supports not (not (transform-origin: 2px)) { +} +@supports not (not (transform-origin: 2px)) { +} +@supports not (not +( +transform-origin: 2px +)) { +} +@supports not (not + +( + +transform-origin: 2px + +)) { +} +@supports (display: flexbox) and (not (display: inline-grid)) { +} +@supports (display: table-cell) and (display: list-item) { +} +@supports (display: table-cell) and (display: list-item) { +} +@supports (display: table-cell) and (display: list-item) { +} +@supports (display: table-cell) and (display: list-item) { +} +@supports (display: table-cell) and (display: list-item) { +} +@supports (display: table-cell) and (display: list-item) { +} +@supports (display: table-cell) and (display: list-item) { +} +@supports (display: table-cell) and (display: list-item) { +} +@supports (display: table-cell) and (display: list-item) and (display: run-in) { +} +@supports (display: table-cell) and ((display: list-item) and (display: run-in)) { +} +@supports (display: table-cell) and ((display: list-item) and (display: run-in)) { +} +@supports (display: table-cell) and ((display: list-item) + and (display: run-in)) { +} +@supports (display: table-cell) and ((display: list-item) and + (display: run-in)) { +} +@supports (display: table-cell) and (( +display: list-item +) +and +( +display +: +run-in +)) { +} +@supports (display: table-cell) and (( + +display: list-item + +) + +and + +( + +display + +: + +run-in + +)) { +} +@supports (transform-style: preserve) or (-moz-transform-style: preserve) { +} +@supports (transform-style: preserve) or (-moz-transform-style: preserve) or (-o-transform-style: preserve) or (-webkit-transform-style: preserve) { +} +@supports (transform-style: preserve-3d) or ((-moz-transform-style: preserve-3d) or ((-o-transform-style: preserve-3d) or (-webkit-transform-style: preserve-3d))) { +} + +`; + +exports[`viewport.css 1`] = ` +@viewport { + min-width: 640px; + max-width: 800px; +} + +@viewport { +min-width: 640px; +max-width: 800px; +} + +@viewport{ + min-width: 640px; + max-width: 800px; +} + +@viewport { + min-width: 640px; + max-width: 800px; +} + +@viewport +{ +min-width +: +640px +; +max-width +: +800px +; +} + +@viewport + +{ + +min-width + +: + +640px + +; + +max-width + +: + +800px + +; + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@viewport { + min-width: 640px; + max-width: 800px; +} + +@viewport { + min-width: 640px; + max-width: 800px; +} + +@viewport { + min-width: 640px; + max-width: 800px; +} + +@viewport { + min-width: 640px; + max-width: 800px; +} + +@viewport { + min-width: 640px; + max-width: 800px; +} + +@viewport { + min-width: 640px; + + max-width: 800px; +} + +`; + +exports[`while.css 1`] = ` +@while $i > 0 {} +@while $i>0{} +@while $i > 0 {} +@while $i + > + 0 + {} +@while + $i + > + 0 + {} +@while +$i +> +0 +{ +} +@while + +$i + +> + +0 + +{ + +} +@while ($i > 0) {} +@while ($i>0){} +@while ( $i > 0 ) {} +@while ( $i > 0 ) {} +@while ( + $i > 0 +) {} +@while + ($i > 0) {} +@while +( +$i +> +0 +) +{ +} +@while + +( + +$i + +> + +0 + +) + +{ + +} +@while $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > 1 {} +@while 1 > $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var {} +@while $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > $other-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var {} +@while (($i) > (0)) {} +@while (($i)>(0)){} +@while ( ( $i ) > ( 0 ) ) {} +@while (($i) + > + (0) +) {} +@while +( +( +$i +) +> +( +0 +) +) +{ +} +@while + +( + +( + +$i + +) + +> + +( + +0 + +) + +) + +{ + +} +@while ($very-very-very-very-very-very-very-very-very-very-very-very-long-var > 1) {} +@while (1 > $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var) {} +@while ($very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > 1) {} +@while ($very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > $other-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var) {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@while $i > 0 { +} +@while $i>0 { +} +@while $i > 0 { +} +@while $i > 0 { +} +@while $i > 0 { +} +@while $i > 0 { +} +@while $i > 0 { +} +@while ($i > 0) { +} +@while ($i>0) { +} +@while ($i > 0) { +} +@while ($i > 0) { +} +@while ($i > 0) { +} +@while ($i > 0) { +} +@while ($i > 0) { +} +@while ($i > 0) { +} +@while $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > + 1 +{ +} +@while 1 > + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var +{ +} +@while $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > + $other-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var +{ +} +@while (($i) > (0)) { +} +@while (($i) >(0)) { +} +@while (($i) > (0)) { +} +@while (($i) > (0)) { +} +@while (($i) > (0)) { +} +@while (($i) > (0)) { +} +@while ( + $very-very-very-very-very-very-very-very-very-very-very-very-long-var > 1 +) { +} +@while ( + 1 > + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var +) { +} +@while ( + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > + 1 +) { +} +@while ( + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > + $other-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var +) { +} `; diff --git a/tests/css_atrule/at-root.css b/tests/css_atrule/at-root.css new file mode 100644 index 00000000..734050cc --- /dev/null +++ b/tests/css_atrule/at-root.css @@ -0,0 +1,292 @@ +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { +@at-root { +.child1 { +width: 100px; +} +.child2 { +width: 200px; +} +} +} +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent { + @at-root { + .child1 { + width: 100px; + } + .child2 { + width: 200px; + } + } +} +.parent +{ +@at-root +{ +.child1 +{ +width +: +100px +; +} +.child2 +{ +width +: +200px +; +} +} +} +.parent + +{ + +@at-root + +{ + +.child1 + +{ + +width + +: + +100px + +; + +} +.child2 + +{ + +width + +: + +200px + +; + +} + +} + +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent { +@at-root .child { +width: 100px; +} +} +.parent{ + @at-root .child{ + width: 100px; + } +} +.parent { + @at-root .child { + width: 100px; + } +} +.parent +{ +@at-root +.child +{ +width +: +100px +; +} +} +.parent + +{ + +@at-root + +.child + +{ + +width + +: + +100px + +; + +} + +} +.parent { +@at-root +input[ +type += +'radio' +] +{ +color +: +red +; +} +} +@media print { + .page { + width: 8in; + + @at-root (with: media) { + color: red; + } + } +} +@media print { +.page { +width: 8in; + +@at-root (with: media) { +color: red; +} +} +} +@media print{ + .page{ + width: 8in; + + @at-root (with:media){ + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root ( with : media ) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root ( with : media ) { + color: red; + } + } +} +@media print { + .page { + width: 8in; + + @at-root + (with: media) { + color: red; + } + } +} +@media print +{ +.page +{ +width +: +8in +; +@at-root +( +with +: +media +) +{ +color +: +red +; +} +} +} +@media print + +{ + +.page + +{ + +width + +: + +8in + +; + +@at-root + +( + +with + +: + +media + +) + +{ + +color +: + +red + +; + +} + +} + +} +@media print { + .page { + width: 8in; + + @at-root (without: media) { + color: red; + } + } +} diff --git a/tests/css_atrule/charset.css b/tests/css_atrule/charset.css new file mode 100644 index 00000000..0ff382c5 --- /dev/null +++ b/tests/css_atrule/charset.css @@ -0,0 +1,6 @@ +@charset "UTF-8"; +@charset "iso-8859-15"; +@charset 'iso-8859-15'; /* Invalid, wrong quoting style used */ +@charset "UTF-8"; /* Invalid, more than one space */ +@charset "UTF-8"; /* Invalid, there is a character (a space) before the at-rule */ +@charset UTF-8; /* Invalid, without ' or ", the charset is not a CSS */ diff --git a/tests/css_atrule/counter-style.css b/tests/css_atrule/counter-style.css new file mode 100644 index 00000000..fc41c5d4 --- /dev/null +++ b/tests/css_atrule/counter-style.css @@ -0,0 +1,69 @@ +@counter-style identifier { + system: cyclic; + symbols: "\1F44D"; + suffix: " "; +} +@counter-style identifier { +system: cyclic; +symbols: "\1F44D"; +suffix: " "; +} +@counter-style identifier{ + system: cyclic; + symbols: "\1F44D"; + suffix: " "; +} +@counter-style identifier { + system: cyclic; + symbols: "\1F44D"; + suffix: " "; +} +@counter-style +identifier +{ +system +: +cyclic +; +symbols +: +"\1F44D" +; +suffix +: +" " +; +} + +@counter-style + +identifier + +{ + +system + +: + +cyclic + +; + +symbols + + +: + +"\1F44D" + +; + +suffix + +: + +" " + +; + +} diff --git a/tests/css_atrule/custom-media.css b/tests/css_atrule/custom-media.css new file mode 100644 index 00000000..8bde7703 --- /dev/null +++ b/tests/css_atrule/custom-media.css @@ -0,0 +1,104 @@ +@custom-media --small-viewport (max-width: 30em); +@custom-media --small-viewport (max-width:30em); +@custom-media --small-viewport ( max-width : 30em ) ; +@custom-media --small-viewport + (max-width: 30em); +@custom-media + --small-viewport + ( + max-width: 30em + ); +@custom-media +--small-viewport +( +max-width +: +30em +) +; +@custom-media + +--small-viewport + +( + +max-width + +: + +30em + +) + +; +@custom-media --none not all; +@custom-media --none not all ; +@custom-media --none + not all; +@custom-media --none + not + all; +@custom-media +--none +not +all +; +@custom-media + +--none + +not + +all + +; +@custom-media --tablet (min-width: 768px) and (max-width: 1279px); +@custom-media --tablet (min-width:768px) and (max-width:1279px); +@custom-media --tablet ( min-width : 768px ) and ( max-width : 1279px ) ; +@custom-media --tablet + (min-width: 768px) and (max-width: 1279px); +@custom-media + --tablet + (min-width: 768px) and + (max-width: 1279px); +@custom-media +--tablet +( +min-width +: +768px +) +and +( +max-width +: +1279px +) +; +@custom-media + +--tablet + +( + +min-width + +: + +768px + +) + +and + +( + +max-width + +: + +1279px + +) + +; diff --git a/tests/css_atrule/custom-selector.css b/tests/css_atrule/custom-selector.css new file mode 100644 index 00000000..4283fb09 --- /dev/null +++ b/tests/css_atrule/custom-selector.css @@ -0,0 +1,64 @@ +@custom-selector :--heading h1, h2, h3, h4, h5, h6; +@custom-selector :--heading h1,h2,h3,h4,h5,h6; +@custom-selector :--heading h1 , h2 , h3 , h4 , h5 , h6 ; +@custom-selector :--heading + h1, h2, h3, h4, h5, h6; +@custom-selector + :--heading + h1, + h2, + h3, + h4, + h5, + h6; +@custom-selector +:--heading +h1 +, +h2 +, +h3 +, +h4 +, +h5 +, +h6 +; +@custom-selector + +:--heading + +h1 + +, + +h2 + +, + +h3 + +, + +h4 + +, + +h5 + +, + +h6 + +; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name h1, h2, h3, h4, h5, h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name h1 + h1, h2, h3, h4, h5, h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name .very-very-very-very-very-very-very-very-very-long-selector-name + very-very-very-very-very-very-very-very-very-long-selector-name-other, h2, h3, h4, h5, h6; +@custom-selector :--very-very-very-very-very-very-very-very-very-long-selector-name h1 h1, h2, h3, h4, h5, h6; +@custom-selector :--heading .very-very-very-very-very-very-very-very-very-very-long-class-name, .very-very-very-very-very-very-very-very-very-very-long-class-name-other; +@custom-selector :--enter :hover, :focus; +@custom-selector :--visible :global.visible; +@custom-selector :--icon i[class^='icon-'], i[class*=' icon-']; +@custom-selector :--placeholder :placehoder-shown, ::-webkit-input-placeholder, ::-moz-placeholder, :-ms-input-placeholder; +@custom-selector :--enter :matches(:hover, :focus, :active); diff --git a/tests/css_atrule/debug.css b/tests/css_atrule/debug.css new file mode 100644 index 00000000..efa0bb6d --- /dev/null +++ b/tests/css_atrule/debug.css @@ -0,0 +1,22 @@ +@debug 10em + 12em; +@debug 10em+12em; +@debug 10em + 12em ; +@debug 10em + + + 12em + ; +@debug +10em ++ +12em +; +@debug + +10em + ++ + +12em + +; +@debug $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var; diff --git a/tests/css_atrule/each.css b/tests/css_atrule/each.css new file mode 100644 index 00000000..132f81af --- /dev/null +++ b/tests/css_atrule/each.css @@ -0,0 +1,217 @@ +@each $animal in puma, sea-slug, egret, salamander {} +@each $animal in puma,sea-slug,egret,salamander{} +@each $animal in puma , sea-slug , egret , salamander {} +@each $animal in + puma, sea-slug, egret, salamander {} +@each + $animal + in + puma + , + sea-slug + , + egret + , + salamander + { + } +@each +$animal +in +puma +, +sea-slug +, +egret +, +salamander +{ +} +@each + +$animal + +in + +puma + +, + +sea-slug + +, + +egret + +, + +salamander + +{ + +} +@each $animal in ((puma), (sea-slug), (egret), (salamander)) {} +@each $animal in((puma),(sea-slug),(egret),(salamander)){} +@each $animal in ( ( puma ) , ( sea-slug ) , ( egret ) , ( salamander ) ) {} +@each $animal + in ((puma), (sea-slug), (egret), (salamander)) {} +@each + $animal + in ( + (puma), + (sea-slug), + (egret), + (salamander) + ) { } +@each + $animal + in + ( + ( + puma + ) + , + ( + sea-slug + ) + , + ( + egret + ) + , + ( + salamander + ) + ) + { + } +@each + + $animal + + in + + ( + + ( + + puma + + ) + + , + + ( + + sea-slug + + ) + + , + + ( + + egret + + ) + + , + + ( + + salamander + + ) + + ) + + { + + } +@each $animal, $color, $cursor in (puma, black, default), (sea-slug, blue, pointer), (egret, white, move) {} +@each $animal,$color,$cursor in (puma,black,default),(sea-slug,blue,pointer),(egret,white,move){} +@each $animal, $color, $cursor in + (puma, black, default), + (sea-slug, blue, pointer), + (egret, white, move) {} +@each $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var in puma, sea-slug, egret, salamander {} +@each $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 in (puma, black, default), (sea-slug, blue, pointer), (egret, white, move) {} +@each $element, $size in (h1: 20px, h2: 16px, h3: 14px) {} +@each $element,$size in(h1:20px,h2:16px,h3:14px){} +@each $element , $size in ( h1 : 20px , h2 : 16px , h3 : 14px ) {} +@each $element, + $size in (h1: 20px, h2: 16px, h3: 14px) {} +@each + $element, + $size + in + ( + h1 + : + 20px + , + h2 + : + 16px + , + h3 + : + 14px + ) + { + } +@each +$element, +$size +in +( +h1 +: +20px +, +h2 +: +16px +, +h3 +: +14px +) +{ +} +@each +$element, + +$size + +in + +( + +h1 + +: + +20px + +, + +h2 + +: + +16px + +, + +h3 + +: + +14px + +) + +{ + +} diff --git a/tests/css_atrule/extend.css b/tests/css_atrule/extend.css new file mode 100644 index 00000000..3fae948e --- /dev/null +++ b/tests/css_atrule/extend.css @@ -0,0 +1,151 @@ +.master { + color: black; + font-size: 12px; +} +.emphasis { + @extend .master; + font-weight: bold; +} +.emphasis { +@extend .master; +font-weight: bold; +} +.emphasis { + @extend .master ; + font-weight: bold; +} +.emphasis { + @extend .master ; + font-weight: bold; +} +.emphasis +{ +@extend +.master +; +font-weight +: +bold +; +} +.emphasis + +{ + +@extend + +.master + +; + +font-weight + +: + +bold + +; + +} +%message-shared { + border: 1px solid #ccc; + padding: 10px; + color: #333; +} +.message { + @extend %message-shared; +} +.message { +@extend %message-shared; +} +.message { + @extend %message-shared ; +} +.message { + @extend %message-shared ; +} +.message { +@extend +%message-shared; +} +.message { + +@extend + +%message-shared; + +} +a.important { + @extend .notice !optional; +} +a.important { +@extend .notice !optional; +} +a.important { + @extend .notice !optional ; +} +a.important { + @extend .notice !optional ; +} +a.important +{ +@extend +.notice +!optional +; +} +a.important + +{ + +@extend + +.notice + +!optional + +; + +} +.message-error { + @extend .message, .important; +} +.message-error { + @extend .message , .important ; +} +.message-error { + @extend .message , .important ; +} +.message-error { + @extend .message, + .important; +} +.message-error { + @extend + .message, + .important; +} +.message-error +{ +@extend +.message +, +.important +; +} +.message-error + +{ + +@extend + +.message + +, + +.important; + +} +.message-error { + @extend .very-very-very-very-very-very-very-very-very-very-very-very-very-long-selector, .very-very-very-very-very-very-very-very-very-very-very-very-very-long-selector; +} diff --git a/tests/css_atrule/font-face.css b/tests/css_atrule/font-face.css new file mode 100644 index 00000000..dca6be54 --- /dev/null +++ b/tests/css_atrule/font-face.css @@ -0,0 +1,89 @@ +@font-face { + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face { +font-family: "Open Sans"; +src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), +url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face{ + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face { + font-family: "Open Sans"; + src: url("/fonts/OpenSans-Regular-webfont.woff2") format("woff2"), + url("/fonts/OpenSans-Regular-webfont.woff") format("woff"); +} +@font-face +{ +font-family +: +"Open Sans" +; +src +: +url( +"/fonts/OpenSans-Regular-webfont.woff2" +) +format( +"woff2" +) +, +url( +"/fonts/OpenSans-Regular-webfont.woff" +) +format( +"woff" +) +; +} +@font-face + +{ + +font-family + +: + +"Open Sans" + +; + +src + +: + +url( + +"/fonts/OpenSans-Regular-webfont.woff2" + +) + +format( + +"woff2" + +) + +, + +url( + +"/fonts/OpenSans-Regular-webfont.woff" + +) + +format( + + +"woff" + +) + +; + +} diff --git a/tests/css_atrule/font-feature-values.css b/tests/css_atrule/font-feature-values.css new file mode 100644 index 00000000..3101051c --- /dev/null +++ b/tests/css_atrule/font-feature-values.css @@ -0,0 +1,52 @@ +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} +@font-feature-values Font One { +@styleset { +nice-style: 12; +} +} +@font-feature-values Font One{ + @styleset{ + nice-style: 12; + } +} +@font-feature-values Font One { + @styleset { + nice-style: 12; + } +} +@font-feature-values +Font One +{ +@styleset +{ +nice-style +: +12 +; +} +} + +@font-feature-values + +Font One + +{ + +@styleset +{ + +nice-style + +: + +12 + +; + +} + +} diff --git a/tests/css_atrule/for.css b/tests/css_atrule/for.css new file mode 100644 index 00000000..265c87f7 --- /dev/null +++ b/tests/css_atrule/for.css @@ -0,0 +1,43 @@ +@for $i from 1 through 8 {} +@for $i from 1 through 8{} +@for $i from 1 through 8 {} +@for $i +from +1 +through +8 +{} +@for +$i +from +1 +through +8 +{} +@for +$i +from +1 +through +8 +{} +@for + +$i + +from + +1 + +through + +8 + +{} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from 1 through 5 {} +@for $i from $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 through 5 {} +@for $i from 1 through $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 {} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 through $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 {} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 end $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 {} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from ($var1 + $var1) through ($var-2 + $var-2) {} +@for $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var from ($very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1 + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var1) through ($very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2 + $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var-2) {} diff --git a/tests/css_atrule/function.css b/tests/css_atrule/function.css new file mode 100644 index 00000000..9e50093b --- /dev/null +++ b/tests/css_atrule/function.css @@ -0,0 +1,88 @@ +@function func($arg, $arg1, $arg2: 10, $args...) { + @return "Func"; +} +@function func($arg,$arg1,$arg2:10,$args...){ + @return "Func"; +} +@function func ( $arg , $arg1 , $arg2 : 10 , $args ... ) { + @return "Func"; +} +@function func ( $arg , $arg1 , $arg2 : 10 , $args ... ) { + @return "Func"; +} +@function func( + $arg, + $arg1, + $arg2: 10, + $args... +) { + @return "Func"; +} +@function + func( + $arg, + $arg1, + $arg2: 10, + $args... + ) { + @return "Func"; +} +@function +func +( +$arg +, +$arg1 +, +$arg2 +: +10 +, +$args +... +) +{ +@return +"Func" +; +} +@function + +func + +( + +$arg + +, + +$arg1 + +, + +$arg2 + +: + +10 + +, + +$args + +... + +) + +{ + +@return + +"Func" + +; + +} +@function func($very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: 10, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-args...) { + @return "Func"; +} diff --git a/tests/css_atrule/if-else.css b/tests/css_atrule/if-else.css index 7b92e70f..df7c6e6a 100644 --- a/tests/css_atrule/if-else.css +++ b/tests/css_atrule/if-else.css @@ -5,3 +5,141 @@ } @else if $media == tabletPortrait { $k: .065106; } +p { + @if $type == ocean {} + @if $type==ocean{} + @if $type == ocean {} + @if $type + == ocean {} + @if + $type + == + ocean + { + } + @if + + $type + + == + + ocean + + { + + } + @if (($type) == (ocean)) {} + @if (($type)==(ocean)){} + @if ( ( $type ) == ( ocean ) ) {} + @if (($type) + == (ocean)) {} + @if + ( + ( + $type + ) + == + ( + ocean + ) + ) + { + } + @if + + ( + + ( + + $type + + ) + + == + + ( + + ocean + + ) + + ) + + { + + } + @if $type == ocean { + color: blue; + } @else if $type == matador { + color: red; + } @else if $type == monster { + color: green; + } @else { + color: black; + } + @if $type == ocean { + } @else if $type == matador { + } + @if $type==ocean{}@else if $type==matador{} + @if $type == ocean { } @else if $type == matador { } + @if $type == ocean {} + @else if $type == matador {} + @if + $type == ocean {} + @else if + $type == matador {} + @if + $type + == + ocean + { + } + @else + if + $type + == + matador + { + } + @if + + $type + + == + + ocean + + { + + } + + @else + + if + + $type + + == + + matador + + { + + } + @if $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables == $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables { + color: blue; + } @else if $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables == $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables { + color: red; + } @else if $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables == $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-variables { + color: green; + } @else { + color: black; + } + @if $very-very-very-long-var == 0 and $very-very-very-long-var == 0 {} + @if $very-very-very-very-very-very-long-var == 0 and $very-very-very-long-var == 0 {} + @if $very-very-very-very-very-very-very-very-very-very-very-long-var == 0 and $very-very-very-very-very-very-very-very-very-very-very-long-var == 0 {} + @if $base-font-size != 16px or $base-line-height != 24px or $base-unit != 'em' or $h1-font-size != 2 * $base-font-size or $h2-font-size != 1.5 * $base-font-size or $h3-font-size != 1.17 * $base-font-size or $h4-font-size != 1 * $base-font-size or $h5-font-size != 0.83 * $base-font-size or $h6-font-size != 0.67 * $base-font-size or $indent-amount != 40px {} + @if (str-slice($item, 0, 1) == ":") {} + @if (str-slice($item, 0, 3) == " : ") {} + @if ($type == ocean) {} @else if ($type == matador) {} @else {} +} diff --git a/tests/css_atrule/import.css b/tests/css_atrule/import.css new file mode 100644 index 00000000..a2f9cf99 --- /dev/null +++ b/tests/css_atrule/import.css @@ -0,0 +1,86 @@ +@import url("fineprint.css") print; +@import url("bluish.css") projection, tv; +@import 'custom.css'; +@import url("chrome://communicator/skin/"); +@import "common.css" screen, projection; +@import url('landscape.css') screen and (orientation:landscape); + +@import (multiple) "foo.less"; + +/* This isn't valid CSS, SCSS or Less, but we should be lenient and make sure +/* that nothing is lost when printing. */ +@import "one" two "three"; + +#main { + @import "example"; +} +#main { +@import "example"; +} +#main { + @import "example" ; +} +#main { + @import "example" ; +} +#main { + @import + "example"; +} +#main +{ +@import +"example"; +} +#main + +{ + +@import + +"example"; + +} + +@import "test.less" {} +@import "test.less" { + a: b; + c: d; +} + +@import url('foo'); +$dir: 'fonts'; + +@import url("foo.css"); +@import url('foo.css'); +@import url("foo.css"); +@import url("foo.css"); +@import url("fineprint.css")print; +@import url("fineprint.css") print; +@import url("fineprint.css") print; +@import url("fineprint.css") + print; +@import url("bluish.css") projection,tv; +@import url("bluish.css") projection, tv; +@import url("bluish.css") projection ,tv; +@import url("bluish.css") projection , tv; +@import url("bluish.css") projection , tv; +@import url("bluish.css") + projection, + tv; +@import url("very-very-very-very-very-very-very-very-very-very-long-name.css") projection,tv; +@import url('landscape.css') screen and (orientation:landscape); +@import "rounded-corners", "text-shadow"; +@import 'rounded-corners', 'text-shadow'; +$family: unquote("Droid+Sans"); +@import url("http://fonts.googleapis.com/css?family=#{$family}"); +@import url( "foo.css"); +@import url("foo.css" ); +@import url( "foo.css" ); +@import url( "foo.css" ); +@import url("foo bar baz.css"); +@import url( "foo bar baz.css" ); +@import url("foo bar baz.css"); +@import url( +"foo.css" +); diff --git a/tests/css_atrule/include.css b/tests/css_atrule/include.css new file mode 100644 index 00000000..0097c1de --- /dev/null +++ b/tests/css_atrule/include.css @@ -0,0 +1,84 @@ +@include mix(1px, 2px, $arg2: 10, 2px 4px 6px); +@include mix(1px,2px,$arg2:10,2px 4px 6px); +@include mix ( 1px , 2px , $arg2 : 10 , 2px 4px 6px ); +@include mix ( 1px , 2px , $arg2 : 10 , 2px 4px 6px ); +@include mix( + 1px, + 2px, + $arg2: 10, + 2px 4px 6px +); +@include +mix( + 1px, + 2px, + $arg2: 10, + 2px 4px 6px +); +@include +mix +( +1px +, +2px +, +$arg2 +: +10 +, +2px +4px +6px +) +; +@include + +mix + +( + +1px + +, + +2px + +, + +$arg2 + +: + +10 + +, + +2px + +4px + +6px + +) + +; +@include mix($very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg: 1px, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1: 2px, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: 10, 2px 4px 6px); +a { + @include global-variable-overriding; + @include mix($very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg: 1px, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1: 2px, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: 10, 2px 4px 6px); + @include apply-to-ie6-only {} + @include apply-to-ie6-only{} + @include apply-to-ie6-only + {} + @include + apply-to-ie6-only + { + } + @include + + apply-to-ie6-only + + { + + } +} diff --git a/tests/css_atrule/keyframes.css b/tests/css_atrule/keyframes.css new file mode 100644 index 00000000..059c3e6d --- /dev/null +++ b/tests/css_atrule/keyframes.css @@ -0,0 +1,226 @@ +@keyframes identifier { + 0% { + top: 0; + left: 0; + } + 30% { + top: 50px; + } + 68%, + 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes identifier { +0%{top:0;left:0;} +30%{top: 50px;} +68%,72%{left: 50px;} +100%{top: 100px; left: 100%;} +} +@keyframes identifier{ + 0% { + top:0; + left:0; + } + 30% { + top: 50px; + } + 68%, 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes identifier { + 0% { + top:0; + left:0; + } + 30% { + top: 50px; + } + 68%, 72% { + left: 50px; + } + 100% { + top: 100px; + left: 100%; + } +} +@keyframes +identifier +{ +0% +{ +top +: +0; +left +: +0 +; +} +30% +{ +top +: +50px +; +} +68% +, +72% +{ +left +: +50px +; +} +100% +{ +top +: +100px +; +left +: +100% +; +} +} +@keyframes + +identifier + +{ + +0% + +{ + +top + +: + +0 + +; + +left + +: + +0 + +; + +} + +30% + +{ + +top + +: + +50px + +; + +} + +68% + +, + +72% + +{ + +left + +: + +50px + +; + +} + +100% + +{ + +top + +: + +100px + +; + +left + +: + +100% + +; + +} + +} +@keyframes identifier { + from { + margin-top: 50px; + } + to { + margin-top: 100px; + } +} +@keyframes +identifier +{ +from +{ +margin-top: 50px; +} +to +{ +margin-top: 100px; +} +} +@keyframes + +identifier + +{ + +from + +{ + +margin-top: 50px; + +} + +to + +{ + +margin-top: 100px; + +} + +} +@-webkit-keyframes identifier { + 0% { opacity: 0; top: 4rem; } + 100% { opacity: 1; top: 0; } +} diff --git a/tests/css_atrule/media.css b/tests/css_atrule/media.css new file mode 100644 index 00000000..9ff29dc6 --- /dev/null +++ b/tests/css_atrule/media.css @@ -0,0 +1,189 @@ +@media screen and (min-width: 480px) {} +@media screen and (min-width: 480px) {} +@MEDIA screen and (min-width: 480px) {} +@MEDIA screen and (min-width: 480px) {} +@media screen and (min-width: 480px) {} +@media screen and (min-width: 480px) {} +@media screen and (min-width :480px) {} +@media screen and (min-width : 480px) {} +@media screen and (min-width : 480px) {} +@media + screen + and + (min-width + : + 480px) {} +@media screen and ( min-width: 480px) {} +@media screen and (min-width: 480px ) {} +@media screen and ( min-width: 480px ) {} +@media screen and ( min-width: 480px ) {} +@media not screen and (color), print and (color) { } +@media not screen and (color) , print and (color) { } +@media not screen and (color),print and (color) { } +@media not screen and (color) , print and (color) { } +@media not screen and (color) , print and (color) { } +@media not screen and ( color), print and (color) { } +@media not screen and (color ), print and (color) { } +@media not screen and ( color ), print and (color) { } +@media not screen and ( color ), print and (color) { } +@media (--small-viewport) {} +@media +( +--small-viewport +) +{ +} +@media + +( + +--small-viewport + +) + +{ + +} + +.screen-color { + @media screen { + color: green; + + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} +.screen-color { +@media screen { +color: green; + +@media (min-width: 768px) { +color: red; +} +} +@media tv { +color: black; +} +} +.screen-color{ + @media screen{ + color: green; + + @media (min-width: 768px){ + color: red; + } + } + @media tv{ + color: black; + } +} +.screen-color { + @media screen { + color: green; + + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} +.screen-color +{ +@media +screen +{ +color +: +green +; +@media +( +min-width +: +768px +) +{ +color +: +red +; +} +} +@media +tv +{ +color +: +black +; +} +} +.screen-color + +{ + +@media + +screen + +{ + +color + +: + +green + +; + +@media + +( + +min-width + +: + +768px + +) + +{ + +color + +: + +red + +; + +} + +} + +@media + +tv + +{ + +color + +: + +black + +; + +} + +} + +@media all and (-webkit-min-device-pixel-ratio: 1.5), all and (-o-min-device-pixel-ratio: 3/2), all and (min--moz-device-pixel-ratio: 1.5), all and (min-device-pixel-ratio: 1.5) {} diff --git a/tests/css_atrule/mixin.css b/tests/css_atrule/mixin.css new file mode 100644 index 00000000..24c175e0 --- /dev/null +++ b/tests/css_atrule/mixin.css @@ -0,0 +1,107 @@ +@mixin clearfix {} +@mixin clearfix{} +@mixin clearfix {} +@mixin clearfix + {} +@mixin + clearfix + {} +@mixin +clearfix +{ +} +@mixin + +clearfix + +{ + +} + +@mixin mix($arg, $arg1, $arg2: 10, $args...) {} +@mixin mix($arg,$arg1,$arg2:10,$args...){} +@mixin mix ( $arg , $arg1 , $arg2 : 10 , $args ... ) {} +@mixin mix ( $arg , $arg1 , $arg2 : 10 , $args ... ) {} +@mixin mix( + $arg, + $arg1, + $arg2: 10, + $args... +) {} +@mixin +mix( + $arg, + $arg1, + $arg2: 10, + $args... + ) {} +@mixin +mix +( +$arg +, +$arg1 +, +$arg2 +: +10 +, +$args +... +) +{ +} +@mixin + +mix + +( + +$arg + +, + +$arg1 + +, + +$arg2 + +: + +10 + +, + +$args + +... + +) + +{ +} +@mixin mix($very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg1, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-arg2: 10, $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-args...) {} +@mixin component($conf: ()) {} +@mixin component($conf: ( )) {} +@mixin button-variant($hover-background: darken($background, 7.5%)) {} +@mixin button-variant( $hover-background : darken( $background , 7.5% ) ) {} +@mixin button-variant($hover-background:darken($background,7.5%)) {} +@mixin button-variant( + $hover-background: darken($background, 7.5%) +) {} +@mixin +button-variant( +$hover-background +: +darken( +$background +, +7.5% +) +) +{} +@mixin button-variant($foo: "...") {} +@mixin button-variant($foo: " ... ") {} +@mixin button-variant($foo: " ... ") {} +@mixin sexy-border($color, $width, $foo: (color: red)) {} diff --git a/tests/css_atrule/namespaces.css b/tests/css_atrule/namespaces.css new file mode 100644 index 00000000..73442cc2 --- /dev/null +++ b/tests/css_atrule/namespaces.css @@ -0,0 +1,42 @@ +@namespace toto "http://toto.example.org"; +@namespace toto url("http://toto.example.org"); +@namespace toto "http://toto.example.org" ; +@namespace toto "http://toto.example.org" ; +@namespace +toto +"http://toto.example.org" +; +@namespace + +toto + +"http://toto.example.org" + +; +@namespace very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-namespace "http://toto.example.org"; +@namespace +very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-namespace +"http://toto.example.org" +; +@namespace + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-namespace + +"http://toto.example.org" + +; +@namespace "http://example.com/foo"; +@namespace url("http://example.com/foo"); +@namespace url("http://example.com/foo") ; +@namespace url("http://example.com/foo") ; +@namespace +url("http://example.com/foo"); +@namespace + +url("http://example.com/foo"); +@namespace url("http://example.com/foo/very/very/very/very/very/very/very/very/very/very/very/very/very/very"); +@namespace +url("http://example.com/foo/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very"); +@namespace + +url("http://example.com/foo/very/very/very/very/very/very/very/very/very/very/very/very/very/very/very"); diff --git a/tests/css_atrule/page.css b/tests/css_atrule/page.css new file mode 100644 index 00000000..3ec8df6c --- /dev/null +++ b/tests/css_atrule/page.css @@ -0,0 +1,69 @@ +@page { + margin: 1cm; +} +@page { +margin: 1cm; +} +@page{ +margin: 1cm; +} +@page { + margin: 1cm; +} +@page +{ +margin +: +1cm +; +} +@page + +{ + +margin +: + +1cm + +; + +} +@page :first { + margin: 2cm; +} +@page :first { +margin: 2cm; +} +@page :first{ + margin: 2cm; +} +@page :first { + margin: 2cm; +} +@page +:first +{ +margin +: +2cm +; +} +@page + +:first + +{ + +margin + +: + +2cm + +; + +} +@page vertical { + size: A4 portrait; +} diff --git a/tests/css_atrule/return.css b/tests/css_atrule/return.css new file mode 100644 index 00000000..e7ae3b8a --- /dev/null +++ b/tests/css_atrule/return.css @@ -0,0 +1,129 @@ +@function grid-width($n) { + @return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { +@return $n * $grid-width + ($n - 1) * $gutter-width / 10; +} +@function grid-width($n) { +@return $n*$grid-width+($n-1)*$gutter-width/10; +} +@function grid-width($n) { + @return $n * $grid-width + ( $n - 1 ) * $gutter-width / 10 ; +} +@function grid-width($n) { + @return $n * $grid-width + ( $n - 1 ) * $gutter-width / 10 ; +} +@function grid-width($n) { + @return $n + * + $grid-width + + ( + $n + - + 1 + ) + * + $gutter-width + / + 10 + ; +} +@function grid-width($n) { + @return + $n + * + $grid-width + + ( + $n + - + 1 + ) + * + $gutter-width + / + 10 + ; +} +@function +grid-width( +$n +) +{ +@return +$n +* +$grid-width ++ +( +$n +- +1 +) +* +$gutter-width +/ +10 +; +} +@function + +grid-width( + +$n + +) + +{ + +@return + +$n + +* + +$grid-width + ++ + +( + +$n + +- + +1 + +) + +* + +$gutter-width + +/ + +10 + +; + +} +@function grid-width($n) { + @return $very-very-very-very-very-very-vey-long-var * $very-very-very-very-very-very-vey-long-var + ($very-very-very-very-very-very-vey-long-var - 1) * $very-very-very-very-very-very-vey-long-var; +} +@function extend($obj, $ext-obj) { + @return map-merge($obj, $ext-obj); +} +@function extend($obj, $ext-obj) { + @return map-merge( $obj , $ext-obj ) ; +} +@function extend($obj, $ext-obj) { + @return map-merge($obj,$ext-obj); +} +@function extend($obj, $ext-obj) { + @return + map-merge( + $obj + , + $ext-obj + ) + ; +} diff --git a/tests/css_atrule/supports.css b/tests/css_atrule/supports.css new file mode 100644 index 00000000..ab339aab --- /dev/null +++ b/tests/css_atrule/supports.css @@ -0,0 +1,280 @@ +@supports (transform-origin: 5% 5%) {} +@supports (transform-origin:5% 5%) {} +@supports ( transform-origin : 5% 5% ) {} +@supports ( transform-origin : 5% 5% ) {} +@supports + (transform-origin: 5% 5%) {} +@supports +(transform-origin: 5% 5%) {} +@supports +( +transform-origin +: +5% +5% +) {} +@supports + +( + +transform-origin + +: + +5% + +5% + +) + +{} +@supports (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports + (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports +( +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop +: +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value +) {} +@supports + +( + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop + +: + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value + +) + +{} +@supports not(transform-origin: 10em 10em 10em) {} +@supports not (transform-origin: 10em 10em 10em) {} +@supports not ( transform-origin : 10em 10em 10em ) {} +@supports not ( transform-origin : 10em 10em 10em ) {} +@supports not + (transform-origin: 10em 10em 10em) {} +@supports + not (transform-origin: 10em 10em 10em) {} +@supports + not + (transform-origin: 10em 10em 10em) {} +@supports +not +( +transform-origin +: +10em +10em +10em +) {} +@supports + +not + +( + +transform-origin + +: + +10em + +10em + +10em + +) + +{} +@supports not (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports + not (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports not + (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value) {} +@supports +not +( +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop +: +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value +) {} +@supports + +not + +( + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-prop + +: + +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-value + +) + +{} +@supports not (not (transform-origin: 2px)) {} +@supports not(not(transform-origin:2px)) {} +@supports not ( not ( transform-origin : 2px ) ) {} +@supports not ( not ( transform-origin : 2px ) ) {} +@supports not + (not (transform-origin: 2px)) {} +@supports not + ( + not (transform-origin: 2px) + ) {} +@supports +not +( +not +( +transform-origin +: +2px +) +) {} +@supports + +not + +( + +not + +( + +transform-origin + +: + +2px + +) + +) + +{} +@supports (display: flexbox) and (not (display: inline-grid)) {} +@supports (display: table-cell) and (display: list-item) {} +@supports ( display : table-cell ) and ( display : list-item ) {} +@supports ( display : table-cell ) and ( display : list-item ) {} +@supports + (display: table-cell) and (display: list-item) {} +@supports (display: table-cell) and + (display: list-item) {} +@supports (display: table-cell) + and (display: list-item) {} +@supports +( +display +: +table-cell +) +and +( +display +: +list-item +) {} +@supports + +( + +display + +: + +table-cell + +) + +and + +( + +display + +: + +list-item + +) + +{} +@supports (display: table-cell) and (display: list-item) and (display: run-in) {} +@supports (display: table-cell) and ((display: list-item) and (display: run-in)) {} +@supports + (display: table-cell) and ((display: list-item) and (display: run-in)) {} +@supports (display: table-cell) + and ((display: list-item) + and (display: run-in)) {} +@supports (display: table-cell) and + ((display: list-item) and + (display: run-in)) {} +@supports +( +display +: +table-cell +) +and +( +( +display +: +list-item +) +and +( +display +: +run-in +) +) {} +@supports + +( + +display + +: + +table-cell + +) + +and + +( + +( + +display + +: + +list-item + +) + +and + +( + +display + +: + +run-in + +) + +) + +{} +@supports (transform-style: preserve) or (-moz-transform-style: preserve) {} +@supports (transform-style: preserve) or (-moz-transform-style: preserve) or (-o-transform-style: preserve) or (-webkit-transform-style: preserve) {} +@supports (transform-style: preserve-3d) or ((-moz-transform-style: preserve-3d) or ((-o-transform-style: preserve-3d) or (-webkit-transform-style: preserve-3d))) {} diff --git a/tests/css_atrule/viewport.css b/tests/css_atrule/viewport.css new file mode 100644 index 00000000..d6a56f13 --- /dev/null +++ b/tests/css_atrule/viewport.css @@ -0,0 +1,53 @@ +@viewport { + min-width: 640px; + max-width: 800px; +} + +@viewport { +min-width: 640px; +max-width: 800px; +} + +@viewport{ + min-width: 640px; + max-width: 800px; +} + +@viewport { + min-width: 640px; + max-width: 800px; +} + +@viewport +{ +min-width +: +640px +; +max-width +: +800px +; +} + +@viewport + +{ + +min-width + +: + +640px + +; + +max-width + +: + +800px + +; + +} diff --git a/tests/css_atrule/while.css b/tests/css_atrule/while.css new file mode 100644 index 00000000..e63a1380 --- /dev/null +++ b/tests/css_atrule/while.css @@ -0,0 +1,110 @@ +@while $i > 0 {} +@while $i>0{} +@while $i > 0 {} +@while $i + > + 0 + {} +@while + $i + > + 0 + {} +@while +$i +> +0 +{ +} +@while + +$i + +> + +0 + +{ + +} +@while ($i > 0) {} +@while ($i>0){} +@while ( $i > 0 ) {} +@while ( $i > 0 ) {} +@while ( + $i > 0 +) {} +@while + ($i > 0) {} +@while +( +$i +> +0 +) +{ +} +@while + +( + +$i + +> + +0 + +) + +{ + +} +@while $very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > 1 {} +@while 1 > $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var {} +@while $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > $other-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var {} +@while (($i) > (0)) {} +@while (($i)>(0)){} +@while ( ( $i ) > ( 0 ) ) {} +@while (($i) + > + (0) +) {} +@while +( +( +$i +) +> +( +0 +) +) +{ +} +@while + +( + +( + +$i + +) + +> + +( + +0 + +) + +) + +{ + +} +@while ($very-very-very-very-very-very-very-very-very-very-very-very-long-var > 1) {} +@while (1 > $very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var) {} +@while ($very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > 1) {} +@while ($very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var > $other-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-long-var) {} diff --git a/tests/css_attribute/__snapshots__/jsfmt.spec.js.snap b/tests/css_attribute/__snapshots__/jsfmt.spec.js.snap index c01564af..41cfcb3a 100644 --- a/tests/css_attribute/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_attribute/__snapshots__/jsfmt.spec.js.snap @@ -1,20 +1,116 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`custom-selector.css 1`] = ` +@custom-selector :--icon i[class^='icon-'], i[class*=' icon-']; +@custom-selector :--icon i[ class ^= 'icon-' ], i[ class *= ' icon-' ]; +@custom-selector +:--icon +i[ +class +^= +'icon-' +] +, +i[ +class +*= +' icon-' +] +; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@custom-selector :--icon i[class^="icon-"], i[class*=" icon-"]; +@custom-selector :--icon i[class^="icon-"], i[class*=" icon-"]; +@custom-selector :--icon i[class^="icon-"], i[class*=" icon-"]; + +`; + +exports[`custom-selector.css 2`] = ` +@custom-selector :--icon i[class^='icon-'], i[class*=' icon-']; +@custom-selector :--icon i[ class ^= 'icon-' ], i[ class *= ' icon-' ]; +@custom-selector +:--icon +i[ +class +^= +'icon-' +] +, +i[ +class +*= +' icon-' +] +; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@custom-selector :--icon i[class^='icon-'], i[class*=' icon-']; +@custom-selector :--icon i[class^='icon-'], i[class*=' icon-']; +@custom-selector :--icon i[class^='icon-'], i[class*=' icon-']; + +`; + exports[`insensitive.css 1`] = ` -input[type="radio" i] { -} +input[type="radio" i] {} +img[alt~="person" i][src*="lorem" i] {} +section:has(:not([type="radio" i], [type="checkbox" i])) {} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ input[type="radio" i] { } +img[alt~="person" i][src*="lorem" i] { +} +section:has(:not([type="radio" i], [type="checkbox" i])) { +} `; exports[`insensitive.css 2`] = ` -input[type="radio" i] { -} +input[type="radio" i] {} +img[alt~="person" i][src*="lorem" i] {} +section:has(:not([type="radio" i], [type="checkbox" i])) {} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ input[type='radio' i] { } +img[alt~='person' i][src*='lorem' i] { +} +section:has(:not([type='radio' i], [type='checkbox' i])) { +} + +`; + +exports[`namespaces.css 1`] = ` +@namespace foo "http://www.example.com"; +[foo|att=val] {} +[*|att] {} +[|att] {} +[att] {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@namespace foo "http://www.example.com"; +[foo|att="val"] { +} +[*|att] { +} +[|att] { +} +[att] { +} + +`; + +exports[`namespaces.css 2`] = ` +@namespace foo "http://www.example.com"; +[foo|att=val] {} +[*|att] {} +[|att] {} +[att] {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@namespace foo 'http://www.example.com'; +[foo|att='val'] { +} +[*|att] { +} +[|att] { +} +[att] { +} `; @@ -23,6 +119,10 @@ a[id=test] {} a[id="test"] {} a[id='test'] {} a[id=func("foo")] {} +a[class="(╯°□°)╯︵ ┻━┻"]{} +input:not([type="radio"]):not([type="checkbox"]) {} +input:not([type="radio"], [type="checkbox"]) {} +section:has(:not([type="radio"], [type="checkbox"])) {} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a[id="test"] { } @@ -32,6 +132,14 @@ a[id="test"] { } a[id=func("foo")] { } +a[class="(╯°□°)╯︵ ┻━┻"] { +} +input:not([type="radio"]):not([type="checkbox"]) { +} +input:not([type="radio"], [type="checkbox"]) { +} +section:has(:not([type="radio"], [type="checkbox"])) { +} `; @@ -40,6 +148,10 @@ a[id=test] {} a[id="test"] {} a[id='test'] {} a[id=func("foo")] {} +a[class="(╯°□°)╯︵ ┻━┻"]{} +input:not([type="radio"]):not([type="checkbox"]) {} +input:not([type="radio"], [type="checkbox"]) {} +section:has(:not([type="radio"], [type="checkbox"])) {} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ a[id='test'] { } @@ -49,5 +161,481 @@ a[id='test'] { } a[id=func('foo')] { } +a[class='(╯°□°)╯︵ ┻━┻'] { +} +input:not([type='radio']):not([type='checkbox']) { +} +input:not([type='radio'], [type='checkbox']) { +} +section:has(:not([type='radio'], [type='checkbox'])) { +} + +`; + +exports[`spaces.css 1`] = ` +[lang] {} +[ lang] {} +[lang ] {} +[ lang ] {} +[ lang ] {} +[ +lang +] {} +span[lang] {} +span[ lang] {} +span[lang ] {} +span[ lang ] {} +span[ lang ] {} +span[lang='pt'] {} +span[lang ='pt'] {} +span[lang= 'pt'] {} +span[lang = 'pt'] {} +span[lang = 'pt'] {} +span[lang='pt' ] {} +span[lang='pt' ] {} +span[ +lang += +'pt' +] {} +span[ lang ~= 'en-us' ] {} +span[ lang ~= 'en-us' ] {} +span[ lang |='zh' ] {} +span[ +lang +~= +'en-us' +] {} +a[ href ^= '#' ] {} +a[ href $= '.cn' ] {} +a[ href *= 'example' ] {} +a[ +href +*= +'example' +] {} +input[ type = 'radio' i ] {} +input[ type = 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ +type +~= +'radio' +i +] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ +alt +~= +'person' +][ +src +*= +'lorem' +] {} +section:has(:not([type='radio'], [type='checkbox'])) {} +section:has(:not([type='radio' i], [type='checkbox' i])) {} +section:has(:not([ type = 'radio' ], [ type = 'checkbox' ])) {} +section:has(:not([ type = 'radio' i ], [ type = 'checkbox' i ])) {} +section:has(:not([ type = 'radio' ], [ type = 'checkbox' ])) {} +section:has(:not([ type = 'radio' i ], [ type = 'checkbox' i ])) {} +section:has(:not([ +type += +'radio' +], [ +type += +'checkbox' +])) {} +section:has(:not([ +type += +'radio' +i +], [ +type += +'checkbox' +i +])) {} +[foo|att=val] {} +[ foo | att = val ] {} +[ foo | att = val ] {} +[ +foo +| +att += +val +] {} +[*|att] {} +[ * | att ] {} +[ * | att ] {} +[ +* +| +att +] {} +[|att] {} +[ | att ] {} +[ | att ] {} +[ +| +att +] {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +[lang] { +} +[lang] { +} +[lang] { +} +[lang] { +} +[lang] { +} +[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang="pt"] { +} +span[lang="pt"] { +} +span[lang="pt"] { +} +span[lang="pt"] { +} +span[lang="pt"] { +} +span[lang="pt"] { +} +span[lang="pt"] { +} +span[lang="pt"] { +} +span[lang~="en-us"] { +} +span[lang~="en-us"] { +} +span[lang|="zh"] { +} +span[lang~="en-us"] { +} +a[href^="#"] { +} +a[href$=".cn"] { +} +a[href*="example"] { +} +a[href*="example"] { +} +input[type="radio" i] { +} +input[type="radio" i] { +} +input[type~="radio" i] { +} +input[type~="radio" i] { +} +input[type~="radio" i] { +} +img[alt="person"][src="lorem"] { +} +img[alt="person"][src="lorem"] { +} +img[alt~="person"][src*="lorem"] { +} +img[alt~="person"][src*="lorem"] { +} +img[alt~="person"][src*="lorem"] { +} +section:has(:not([type="radio"], [type="checkbox"])) { +} +section:has(:not([type="radio" i], [type="checkbox" i])) { +} +section:has(:not([type="radio"], [type="checkbox"])) { +} +section:has(:not([type="radio" i], [type="checkbox" i])) { +} +section:has(:not([type="radio"], [type="checkbox"])) { +} +section:has(:not([type="radio" i], [type="checkbox" i])) { +} +section:has(:not([type="radio"], [type="checkbox"])) { +} +section:has(:not([type="radio" i], [type="checkbox" i])) { +} +[foo|att="val"] { +} +[foo|att="val"] { +} +[foo|att="val"] { +} +[foo|att="val"] { +} +[*|att] { +} +[*|att] { +} +[*|att] { +} +[*|att] { +} +[|att] { +} +[|att] { +} +[|att] { +} +[|att] { +} + +`; + +exports[`spaces.css 2`] = ` +[lang] {} +[ lang] {} +[lang ] {} +[ lang ] {} +[ lang ] {} +[ +lang +] {} +span[lang] {} +span[ lang] {} +span[lang ] {} +span[ lang ] {} +span[ lang ] {} +span[lang='pt'] {} +span[lang ='pt'] {} +span[lang= 'pt'] {} +span[lang = 'pt'] {} +span[lang = 'pt'] {} +span[lang='pt' ] {} +span[lang='pt' ] {} +span[ +lang += +'pt' +] {} +span[ lang ~= 'en-us' ] {} +span[ lang ~= 'en-us' ] {} +span[ lang |='zh' ] {} +span[ +lang +~= +'en-us' +] {} +a[ href ^= '#' ] {} +a[ href $= '.cn' ] {} +a[ href *= 'example' ] {} +a[ +href +*= +'example' +] {} +input[ type = 'radio' i ] {} +input[ type = 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ +type +~= +'radio' +i +] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ +alt +~= +'person' +][ +src +*= +'lorem' +] {} +section:has(:not([type='radio'], [type='checkbox'])) {} +section:has(:not([type='radio' i], [type='checkbox' i])) {} +section:has(:not([ type = 'radio' ], [ type = 'checkbox' ])) {} +section:has(:not([ type = 'radio' i ], [ type = 'checkbox' i ])) {} +section:has(:not([ type = 'radio' ], [ type = 'checkbox' ])) {} +section:has(:not([ type = 'radio' i ], [ type = 'checkbox' i ])) {} +section:has(:not([ +type += +'radio' +], [ +type += +'checkbox' +])) {} +section:has(:not([ +type += +'radio' +i +], [ +type += +'checkbox' +i +])) {} +[foo|att=val] {} +[ foo | att = val ] {} +[ foo | att = val ] {} +[ +foo +| +att += +val +] {} +[*|att] {} +[ * | att ] {} +[ * | att ] {} +[ +* +| +att +] {} +[|att] {} +[ | att ] {} +[ | att ] {} +[ +| +att +] {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +[lang] { +} +[lang] { +} +[lang] { +} +[lang] { +} +[lang] { +} +[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang='pt'] { +} +span[lang~='en-us'] { +} +span[lang~='en-us'] { +} +span[lang|='zh'] { +} +span[lang~='en-us'] { +} +a[href^='#'] { +} +a[href$='.cn'] { +} +a[href*='example'] { +} +a[href*='example'] { +} +input[type='radio' i] { +} +input[type='radio' i] { +} +input[type~='radio' i] { +} +input[type~='radio' i] { +} +input[type~='radio' i] { +} +img[alt='person'][src='lorem'] { +} +img[alt='person'][src='lorem'] { +} +img[alt~='person'][src*='lorem'] { +} +img[alt~='person'][src*='lorem'] { +} +img[alt~='person'][src*='lorem'] { +} +section:has(:not([type='radio'], [type='checkbox'])) { +} +section:has(:not([type='radio' i], [type='checkbox' i])) { +} +section:has(:not([type='radio'], [type='checkbox'])) { +} +section:has(:not([type='radio' i], [type='checkbox' i])) { +} +section:has(:not([type='radio'], [type='checkbox'])) { +} +section:has(:not([type='radio' i], [type='checkbox' i])) { +} +section:has(:not([type='radio'], [type='checkbox'])) { +} +section:has(:not([type='radio' i], [type='checkbox' i])) { +} +[foo|att='val'] { +} +[foo|att='val'] { +} +[foo|att='val'] { +} +[foo|att='val'] { +} +[*|att] { +} +[*|att] { +} +[*|att] { +} +[*|att] { +} +[|att] { +} +[|att] { +} +[|att] { +} +[|att] { +} `; diff --git a/tests/css_attribute/custom-selector.css b/tests/css_attribute/custom-selector.css new file mode 100644 index 00000000..17469991 --- /dev/null +++ b/tests/css_attribute/custom-selector.css @@ -0,0 +1,16 @@ +@custom-selector :--icon i[class^='icon-'], i[class*=' icon-']; +@custom-selector :--icon i[ class ^= 'icon-' ], i[ class *= ' icon-' ]; +@custom-selector +:--icon +i[ +class +^= +'icon-' +] +, +i[ +class +*= +' icon-' +] +; diff --git a/tests/css_attribute/insensitive.css b/tests/css_attribute/insensitive.css index c3fce765..46623f35 100644 --- a/tests/css_attribute/insensitive.css +++ b/tests/css_attribute/insensitive.css @@ -1,2 +1,3 @@ -input[type="radio" i] { -} +input[type="radio" i] {} +img[alt~="person" i][src*="lorem" i] {} +section:has(:not([type="radio" i], [type="checkbox" i])) {} diff --git a/tests/css_attribute/namespaces.css b/tests/css_attribute/namespaces.css new file mode 100644 index 00000000..0450ae3f --- /dev/null +++ b/tests/css_attribute/namespaces.css @@ -0,0 +1,5 @@ +@namespace foo "http://www.example.com"; +[foo|att=val] {} +[*|att] {} +[|att] {} +[att] {} diff --git a/tests/css_attribute/quotes.css b/tests/css_attribute/quotes.css index 6afdac21..70dcc507 100644 --- a/tests/css_attribute/quotes.css +++ b/tests/css_attribute/quotes.css @@ -2,3 +2,7 @@ a[id=test] {} a[id="test"] {} a[id='test'] {} a[id=func("foo")] {} +a[class="(╯°□°)╯︵ ┻━┻"]{} +input:not([type="radio"]):not([type="checkbox"]) {} +input:not([type="radio"], [type="checkbox"]) {} +section:has(:not([type="radio"], [type="checkbox"])) {} diff --git a/tests/css_attribute/spaces.css b/tests/css_attribute/spaces.css new file mode 100644 index 00000000..8e5b130c --- /dev/null +++ b/tests/css_attribute/spaces.css @@ -0,0 +1,115 @@ +[lang] {} +[ lang] {} +[lang ] {} +[ lang ] {} +[ lang ] {} +[ +lang +] {} +span[lang] {} +span[ lang] {} +span[lang ] {} +span[ lang ] {} +span[ lang ] {} +span[lang='pt'] {} +span[lang ='pt'] {} +span[lang= 'pt'] {} +span[lang = 'pt'] {} +span[lang = 'pt'] {} +span[lang='pt' ] {} +span[lang='pt' ] {} +span[ +lang += +'pt' +] {} +span[ lang ~= 'en-us' ] {} +span[ lang ~= 'en-us' ] {} +span[ lang |='zh' ] {} +span[ +lang +~= +'en-us' +] {} +a[ href ^= '#' ] {} +a[ href $= '.cn' ] {} +a[ href *= 'example' ] {} +a[ +href +*= +'example' +] {} +input[ type = 'radio' i ] {} +input[ type = 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ type ~= 'radio' i ] {} +input[ +type +~= +'radio' +i +] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt = 'person' ][ src = 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ alt ~= 'person' ][ src *= 'lorem' ] {} +img[ +alt +~= +'person' +][ +src +*= +'lorem' +] {} +section:has(:not([type='radio'], [type='checkbox'])) {} +section:has(:not([type='radio' i], [type='checkbox' i])) {} +section:has(:not([ type = 'radio' ], [ type = 'checkbox' ])) {} +section:has(:not([ type = 'radio' i ], [ type = 'checkbox' i ])) {} +section:has(:not([ type = 'radio' ], [ type = 'checkbox' ])) {} +section:has(:not([ type = 'radio' i ], [ type = 'checkbox' i ])) {} +section:has(:not([ +type += +'radio' +], [ +type += +'checkbox' +])) {} +section:has(:not([ +type += +'radio' +i +], [ +type += +'checkbox' +i +])) {} +[foo|att=val] {} +[ foo | att = val ] {} +[ foo | att = val ] {} +[ +foo +| +att += +val +] {} +[*|att] {} +[ * | att ] {} +[ * | att ] {} +[ +* +| +att +] {} +[|att] {} +[ | att ] {} +[ | att ] {} +[ +| +att +] {} diff --git a/tests/css_case/__snapshots__/jsfmt.spec.js.snap b/tests/css_case/__snapshots__/jsfmt.spec.js.snap index 4435c485..d066aac9 100644 --- a/tests/css_case/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_case/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,135 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`case.css 1`] = ` +@custom-media --KeepName (MIN-WIDTH: 500PX); + +.foo { + color: hsl(0.75TURN, 60%, 70%); +} + +p:FIRST-CHILD { + color: lime; + background-color: black; + padding: 5px; +} + +a::AFTER { + content: "→"; +} + +a:AFTER { + content: "→"; +} + +::-WEBKIT-PROGRESS-BAR { + background-color: orange; +} + +TABLE {} + +/* apply a dashed border to all unresolved elements */ +:unresolved { + border: 1px dashed red; + display: inline-block; +} + +/* x-panel's that are unresolved are red */ +x-panel:unresolved { + color: red; +} + +/* once the definition of x-panel is registered, it becomes green */ +x-panel { + color: green; + display: block; + padding: 5px; + display: block; +} + +:host { + all: initial; + display: block; + contain: content; + text-align: center; + background: linear-gradient(to left, hotpink, transparent); + max-width: 500px; + margin: 0 auto; + border-radius: 8px; + transition: transform .2s ease-out; +} +:host([hidden]) { + display: none; +} +:host(:hover) { + transform: scale(1.1); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@custom-media --KeepName (min-width: 500px); + +.foo { + color: hsl(0.75turn, 60%, 70%); +} + +p:first-child { + color: lime; + background-color: black; + padding: 5px; +} + +a::after { + content: "→"; +} + +a:after { + content: "→"; +} + +::-webkit-progress-bar { + background-color: orange; +} + +table { +} + +/* apply a dashed border to all unresolved elements */ +:unresolved { + border: 1px dashed red; + display: inline-block; +} + +/* x-panel's that are unresolved are red */ +x-panel:unresolved { + color: red; +} + +/* once the definition of x-panel is registered, it becomes green */ +x-panel { + color: green; + display: block; + padding: 5px; + display: block; +} + +:host { + all: initial; + display: block; + contain: content; + text-align: center; + background: linear-gradient(to left, hotpink, transparent); + max-width: 500px; + margin: 0 auto; + border-radius: 8px; + transition: transform 0.2s ease-out; +} +:host([hidden]) { + display: none; +} +:host(:hover) { + transform: scale(1.1); +} + +`; + exports[`case.less 1`] = ` // Convention in this test file: // - The case should be preserved for things prefixed with "Keep". @@ -21,6 +151,14 @@ svg[viewBox] linearGradient, background: Var(--Keep-custom-Prop); animation-name: KeepAnimationName; important: something !IMPORTANT; + font-family: INITIAL; + padding: UNSET; + border: INHERIT; + all: REVERT; + -WEBKIT-USER-SELECT: none; + -MOZ-USER-SELECT: none; + -MS-USER-SELECT: none; + USER-SELECT: none; } @keyframes KeepAnimationName { @@ -66,7 +204,6 @@ $KeepScssVar: val; } @KeepDetachedRuleset(); - @KeepDetachedRuleset ( /* comment */ ); &:EXTEND(.Keep ALL); @@ -88,6 +225,56 @@ $KeepScssVar: val; @MEDIA (MIN-WIDTH: 700PX) /*:*/ {} @MEDIA (@{Keep}: @{Keep}) {} + +@KEYFRAMES identifier { + FROM { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + TO { + margin-top: 100px; + } +} + +@-WEBKIT-KEYFRAMES float-up { + FROM { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + TO { + margin-top: 100px; + } +} + +.foo { + color: hsl(0.75TURN, 60%, 70%); +} + +p:FIRST-CHILD { + color: lime; + background-color: black; + padding: 5px; +} + +a::AFTER { + content: "→"; +} + +a:AFTER { + content: "→"; +} + +::-WEBKIT-PROGRESS-BAR { + background-color: orange; +} + +TABLE {} + +.foo { &-KeepSelector {} &-KeepSelector & .KeepClassSelector {} &-100\\.200 {} } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Convention in this test file: // - The case should be preserved for things prefixed with "Keep". @@ -98,7 +285,7 @@ $KeepScssVar: val; @import Keep; -HTML#KeepId.KeepClass, +html#KeepId.KeepClass, a[HREF="KeepAttrValue"]:hover::first-letter, svg[viewBox] linearGradient, :not(:nth-child(2n + 1)) { @@ -109,10 +296,18 @@ svg[viewBox] linearGradient, background: Var(--Keep-custom-Prop); animation-name: KeepAnimationName; important: something !IMPORTANT; + font-family: initial; + padding: unset; + border: inherit; + all: revert; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } @keyframes KeepAnimationName { - FROM { + from { prop: val; } @@ -120,7 +315,7 @@ svg[viewBox] linearGradient, prop: val; } - TO { + to { prop: val; } } @@ -129,9 +324,9 @@ svg[viewBox] linearGradient, @media (--KeepCustomMedia) { } -@KeepDetachedRuleset: { +@KeepDetachedRuleset: /*:*/ { background: RED; -} +}; %KeepScssPlaceholderSelector { prop: val; @@ -140,7 +335,7 @@ svg[viewBox] linearGradient, @KeepTopLevelVar: val; $KeepScssVar: val; -.Keep(@Keep: 12e3px) when (@Keep=Case) { +.Keep(@Keep: 12e03PX) when (@Keep=Case) /*:*/ { @KeepVar: KeepName; /*:*/ @{KeepInterpolationVar}: val; $KeepScssVar: val; @@ -150,11 +345,10 @@ $KeepScssVar: val; prop: val; } - &Keep & Element { + &Keep & element { prop: val; } - @KeepDetachedRuleset(); @KeepDetachedRuleset(); &: EXTEND(.Keep ALL); @@ -174,17 +368,75 @@ $KeepScssVar: val; .Keep (@Keep) when (lightness(@Keep) >= Keep12PX) and (@Keep > @Keep12E5) { } -.Keep(@Keep: 12px; @Keep: @Keep12PX; ...) { +.Keep(@Keep: 12PX; @Keep: @Keep12PX; ...) /*:*/ { } -.Keep(@Keep: "12PX"; @Keep: "12PX"; ...) { +.Keep(@Keep: '12PX'; @Keep: "12PX"; ...) /*:*/ { } -@media (min-width: 700px) { +@media (min-width: 700px) /*:*/ { } @media (@{Keep}: @{Keep}) { } +@keyframes identifier { + from { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + to { + margin-top: 100px; + } +} + +@-webkit-keyframes float-up { + from { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + to { + margin-top: 100px; + } +} + +.foo { + color: hsl(0.75turn, 60%, 70%); +} + +p:first-child { + color: lime; + background-color: black; + padding: 5px; +} + +a::after { + content: "→"; +} + +a:after { + content: "→"; +} + +::-webkit-progress-bar { + background-color: orange; +} + +table { +} + +.foo { + &-KeepSelector { + } + &-KeepSelector & .KeepClassSelector { + } + &-100\\.200 { + } +} + `; exports[`case.scss 1`] = ` @@ -207,6 +459,14 @@ svg[viewBox] linearGradient, background: Var(--Keep-custom-Prop); animation-name: KeepAnimationName; important: something !IMPORTANT; + font-family: INITIAL; + padding: UNSET; + border: INHERIT; + all: REVERT; + -WEBKIT-USER-SELECT: none; + -MOZ-USER-SELECT: none; + -MS-USER-SELECT: none; + USER-SELECT: none; } @keyframes KeepAnimationName { @@ -228,8 +488,8 @@ svg[viewBox] linearGradient, @custom-media --KeepCustomMedia screen and (width >= 768px); @media (--KeepCustomMedia) {} -@FUNCTION KeepFuncName() { - @RETURN 12; +@function KeepFuncName() { + @return 12; } %KeepPlaceholderSelector { @@ -239,7 +499,7 @@ svg[viewBox] linearGradient, $KeepTopLevelVar: val; @KeepLessVar: val; -@MIXIN KeepMixinName($Keep: 15IN, $Keep: $Keep15IN, $Keep: Keep-1E-2Em) { +@mixin KeepMixinName($Keep: 15IN, $Keep: $Keep15IN, $Keep: Keep-1E-2Em) { $KeepVar: KeepFuncName(); #{$KeepInterpolationVar}: val; #{$Keep + 15PX}: val; @@ -268,7 +528,7 @@ $KeepTopLevelVar: val; } } -@Mixin Keep($Keep: $Keep15IN, $Keep: Keepå1E1) {} +@mixin Keep($Keep: $Keep15IN, $Keep: Keepå1E1) {} @MEDIA (MIN-WIDTH: 700PX) { @include Keep; @@ -277,6 +537,56 @@ $KeepTopLevelVar: val; } @MEDIA (#{$Keep}: #{$Keep}) {} + +@KEYFRAMES identifier { + FROM { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + TO { + margin-top: 100px; + } +} + +@-WEBKIT-KEYFRAMES float-up { + FROM { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + TO { + margin-top: 100px; + } +} + +.foo { + color: hsl(0.75TURN, 60%, 70%); +} + +p:FIRST-CHILD { + color: lime; + background-color: black; + padding: 5px; +} + +a::AFTER { + content: "→"; +} + +a:AFTER { + content: "→"; +} + +::-WEBKIT-PROGRESS-BAR { + background-color: orange; +} + +TABLE {} + +.foo { &-KeepSelector {} &-KeepSelector & .KeepClassSelector {} &-100\\.200 {} } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Convention in this test file: // - The case should be preserved for things prefixed with "Keep". @@ -286,7 +596,7 @@ $KeepTopLevelVar: val; @import Keep; -HTML#KeepId.KeepClass, +html#KeepId.KeepClass, a[HREF="KeepAttrValue"]:hover::first-letter, svg[viewBox] linearGradient, :not(:nth-child(2n + 1)) { @@ -297,10 +607,18 @@ svg[viewBox] linearGradient, background: Var(--Keep-custom-Prop); animation-name: KeepAnimationName; important: something !important; + font-family: initial; + padding: unset; + border: inherit; + all: revert; + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } @keyframes KeepAnimationName { - FROM { + from { prop: val; } @@ -310,7 +628,7 @@ svg[viewBox] linearGradient, prop: val; } - TO { + to { prop: val; } } @@ -342,7 +660,7 @@ $KeepTopLevelVar: val; prop: val; } - &Keep & Element { + &Keep & element { prop: val; } @@ -371,4 +689,77 @@ $KeepTopLevelVar: val; @media (#{$Keep}: #{$Keep}) { } +@keyframes identifier { + from { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + to { + margin-top: 100px; + } +} + +@-webkit-keyframes float-up { + from { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + to { + margin-top: 100px; + } +} + +.foo { + color: hsl(0.75turn, 60%, 70%); +} + +p:first-child { + color: lime; + background-color: black; + padding: 5px; +} + +a::after { + content: "→"; +} + +a:after { + content: "→"; +} + +::-webkit-progress-bar { + background-color: orange; +} + +table { +} + +.foo { + &-KeepSelector { + } + &-KeepSelector & .KeepClassSelector { + } + &-100\\.200 { + } +} + +`; + +exports[`custom-selectors.css 1`] = ` +@custom-selector :--camelCase .my-css-selector; + +:--camelCase { + content: red; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@custom-selector :--camelCase .my-css-selector; + +:--camelCase { + content: red; +} + `; diff --git a/tests/css_case/case.css b/tests/css_case/case.css new file mode 100644 index 00000000..20e146b8 --- /dev/null +++ b/tests/css_case/case.css @@ -0,0 +1,62 @@ +@custom-media --KeepName (MIN-WIDTH: 500PX); + +.foo { + color: hsl(0.75TURN, 60%, 70%); +} + +p:FIRST-CHILD { + color: lime; + background-color: black; + padding: 5px; +} + +a::AFTER { + content: "→"; +} + +a:AFTER { + content: "→"; +} + +::-WEBKIT-PROGRESS-BAR { + background-color: orange; +} + +TABLE {} + +/* apply a dashed border to all unresolved elements */ +:unresolved { + border: 1px dashed red; + display: inline-block; +} + +/* x-panel's that are unresolved are red */ +x-panel:unresolved { + color: red; +} + +/* once the definition of x-panel is registered, it becomes green */ +x-panel { + color: green; + display: block; + padding: 5px; + display: block; +} + +:host { + all: initial; + display: block; + contain: content; + text-align: center; + background: linear-gradient(to left, hotpink, transparent); + max-width: 500px; + margin: 0 auto; + border-radius: 8px; + transition: transform .2s ease-out; +} +:host([hidden]) { + display: none; +} +:host(:hover) { + transform: scale(1.1); +} diff --git a/tests/css_case/case.less b/tests/css_case/case.less index 28ccd5ce..1e1ecaa9 100644 --- a/tests/css_case/case.less +++ b/tests/css_case/case.less @@ -18,6 +18,14 @@ svg[viewBox] linearGradient, background: Var(--Keep-custom-Prop); animation-name: KeepAnimationName; important: something !IMPORTANT; + font-family: INITIAL; + padding: UNSET; + border: INHERIT; + all: REVERT; + -WEBKIT-USER-SELECT: none; + -MOZ-USER-SELECT: none; + -MS-USER-SELECT: none; + USER-SELECT: none; } @keyframes KeepAnimationName { @@ -63,7 +71,6 @@ $KeepScssVar: val; } @KeepDetachedRuleset(); - @KeepDetachedRuleset ( /* comment */ ); &:EXTEND(.Keep ALL); @@ -85,3 +92,53 @@ $KeepScssVar: val; @MEDIA (MIN-WIDTH: 700PX) /*:*/ {} @MEDIA (@{Keep}: @{Keep}) {} + +@KEYFRAMES identifier { + FROM { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + TO { + margin-top: 100px; + } +} + +@-WEBKIT-KEYFRAMES float-up { + FROM { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + TO { + margin-top: 100px; + } +} + +.foo { + color: hsl(0.75TURN, 60%, 70%); +} + +p:FIRST-CHILD { + color: lime; + background-color: black; + padding: 5px; +} + +a::AFTER { + content: "→"; +} + +a:AFTER { + content: "→"; +} + +::-WEBKIT-PROGRESS-BAR { + background-color: orange; +} + +TABLE {} + +.foo { &-KeepSelector {} &-KeepSelector & .KeepClassSelector {} &-100\.200 {} } diff --git a/tests/css_case/case.scss b/tests/css_case/case.scss index ea8a7733..4dab8f4e 100644 --- a/tests/css_case/case.scss +++ b/tests/css_case/case.scss @@ -17,6 +17,14 @@ svg[viewBox] linearGradient, background: Var(--Keep-custom-Prop); animation-name: KeepAnimationName; important: something !IMPORTANT; + font-family: INITIAL; + padding: UNSET; + border: INHERIT; + all: REVERT; + -WEBKIT-USER-SELECT: none; + -MOZ-USER-SELECT: none; + -MS-USER-SELECT: none; + USER-SELECT: none; } @keyframes KeepAnimationName { @@ -38,8 +46,8 @@ svg[viewBox] linearGradient, @custom-media --KeepCustomMedia screen and (width >= 768px); @media (--KeepCustomMedia) {} -@FUNCTION KeepFuncName() { - @RETURN 12; +@function KeepFuncName() { + @return 12; } %KeepPlaceholderSelector { @@ -49,7 +57,7 @@ svg[viewBox] linearGradient, $KeepTopLevelVar: val; @KeepLessVar: val; -@MIXIN KeepMixinName($Keep: 15IN, $Keep: $Keep15IN, $Keep: Keep-1E-2Em) { +@mixin KeepMixinName($Keep: 15IN, $Keep: $Keep15IN, $Keep: Keep-1E-2Em) { $KeepVar: KeepFuncName(); #{$KeepInterpolationVar}: val; #{$Keep + 15PX}: val; @@ -78,7 +86,7 @@ $KeepTopLevelVar: val; } } -@Mixin Keep($Keep: $Keep15IN, $Keep: Keepå1E1) {} +@mixin Keep($Keep: $Keep15IN, $Keep: Keepå1E1) {} @MEDIA (MIN-WIDTH: 700PX) { @include Keep; @@ -87,3 +95,53 @@ $KeepTopLevelVar: val; } @MEDIA (#{$Keep}: #{$Keep}) {} + +@KEYFRAMES identifier { + FROM { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + TO { + margin-top: 100px; + } +} + +@-WEBKIT-KEYFRAMES float-up { + FROM { + margin-top: 50px; + } + 50% { + margin-top: 150px; + } + TO { + margin-top: 100px; + } +} + +.foo { + color: hsl(0.75TURN, 60%, 70%); +} + +p:FIRST-CHILD { + color: lime; + background-color: black; + padding: 5px; +} + +a::AFTER { + content: "→"; +} + +a:AFTER { + content: "→"; +} + +::-WEBKIT-PROGRESS-BAR { + background-color: orange; +} + +TABLE {} + +.foo { &-KeepSelector {} &-KeepSelector & .KeepClassSelector {} &-100\.200 {} } diff --git a/tests/css_case/custom-selectors.css b/tests/css_case/custom-selectors.css new file mode 100644 index 00000000..30176f54 --- /dev/null +++ b/tests/css_case/custom-selectors.css @@ -0,0 +1,5 @@ +@custom-selector :--camelCase .my-css-selector; + +:--camelCase { + content: red; +} diff --git a/tests/css_color/__snapshots__/jsfmt.spec.js.snap b/tests/css_color/__snapshots__/jsfmt.spec.js.snap index 650ce53e..95019d5d 100644 --- a/tests/css_color/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_color/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,37 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`current-color.css 1`] = ` +.foo { + border: 1px dashed currentColor; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.foo { + border: 1px dashed currentColor; +} + +`; + +exports[`functional-syntax.css 1`] = ` +.foo { + color: rgb(255, 0, 153); + color: rgb(100%, 0%, 60%); + color: rgba(51, 170, 51, 0.1); + color: hsl(.75turn, 60%, 70%); + color: hsl(270 60% 50% / .15); + color: hsla(240, 100%, 50%, .05) +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.foo { + color: rgb(255, 0, 153); + color: rgb(100%, 0%, 60%); + color: rgba(51, 170, 51, 0.1); + color: hsl(0.75turn, 60%, 70%); + color: hsl(270 60% 50% / 0.15); + color: hsla(240, 100%, 50%, 0.05); +} + +`; + exports[`hexcolor.css 1`] = ` .foo { color: #AAA; @@ -7,6 +39,8 @@ exports[`hexcolor.css 1`] = ` -webkit-color: #873; -moz-color: #6bcdef; -ms-color: #AAbbCC; + color: #F09F; + color: #FF0099FF; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .foo { @@ -15,6 +49,27 @@ exports[`hexcolor.css 1`] = ` -webkit-color: #873; -moz-color: #6bcdef; -ms-color: #aabbcc; + color: #f09f; + color: #ff0099ff; +} + +`; + +exports[`whitespace-syntax.css 1`] = ` +.foo { + color: rgba(51 170 51 / 0.4); + color: rgba(51 170 51 / 40%); + color: hsl(270 60% 50% / .15); + color: hsla(240 100% 50% / .05); + color: hsla(240 100% 50% / 5%); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.foo { + color: rgba(51 170 51 / 0.4); + color: rgba(51 170 51 / 40%); + color: hsl(270 60% 50% / 0.15); + color: hsla(240 100% 50% / 0.05); + color: hsla(240 100% 50% / 5%); } `; diff --git a/tests/css_color/current-color.css b/tests/css_color/current-color.css new file mode 100644 index 00000000..d9ba0352 --- /dev/null +++ b/tests/css_color/current-color.css @@ -0,0 +1,3 @@ +.foo { + border: 1px dashed currentColor; +} diff --git a/tests/css_color/functional-syntax.css b/tests/css_color/functional-syntax.css new file mode 100644 index 00000000..b05eef84 --- /dev/null +++ b/tests/css_color/functional-syntax.css @@ -0,0 +1,8 @@ +.foo { + color: rgb(255, 0, 153); + color: rgb(100%, 0%, 60%); + color: rgba(51, 170, 51, 0.1); + color: hsl(.75turn, 60%, 70%); + color: hsl(270 60% 50% / .15); + color: hsla(240, 100%, 50%, .05) +} diff --git a/tests/css_color/hexcolor.css b/tests/css_color/hexcolor.css index 7e86690c..4c93ac3b 100644 --- a/tests/css_color/hexcolor.css +++ b/tests/css_color/hexcolor.css @@ -4,4 +4,6 @@ -webkit-color: #873; -moz-color: #6bcdef; -ms-color: #AAbbCC; + color: #F09F; + color: #FF0099FF; } diff --git a/tests/css_color/whitespace-syntax.css b/tests/css_color/whitespace-syntax.css new file mode 100644 index 00000000..11b01803 --- /dev/null +++ b/tests/css_color/whitespace-syntax.css @@ -0,0 +1,7 @@ +.foo { + color: rgba(51 170 51 / 0.4); + color: rgba(51 170 51 / 40%); + color: hsl(270 60% 50% / .15); + color: hsla(240 100% 50% / .05); + color: hsla(240 100% 50% / 5%); +} diff --git a/tests/css_comment_in_if/__snapshots__/jsfmt.spec.js.snap b/tests/css_comment_in_if/__snapshots__/jsfmt.spec.js.snap deleted file mode 100644 index 4c0cda3a..00000000 --- a/tests/css_comment_in_if/__snapshots__/jsfmt.spec.js.snap +++ /dev/null @@ -1,28 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`comment-in-if.css 1`] = ` -@function _normalize-include($section) { -// Check if $section is in the $include list. - @if index($_normalize-include, $section) { - @return true; - } -// If $include is set to (all), make sure $section is not in $exclude. - @else if not index($_normalize-exclude, $section) and index($_normalize-include, all) { - @return true; - } - @return false; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -@function _normalize-include($section) { - // Check if $section is in the $include list. - @if index($_normalize-include, $section) { - @return true; - } - // If $include is set to (all), make sure $section is not in $exclude. - @else if not index($_normalize-exclude, $section) and index($_normalize-include, all) { - @return true; - } - @return false; -} - -`; diff --git a/tests/css_comments/CRLF.css b/tests/css_comments/CRLF.css new file mode 100644 index 00000000..2a15b032 --- /dev/null +++ b/tests/css_comments/CRLF.css @@ -0,0 +1,19 @@ +/* + * Comment 1 + */ +#header { + /* + * Comment 2 + */ + color: red; +} + +@media only screen and (max-width: 600px) { + /* + * Comment 3 + */ + body { + background-color: lightblue; /* Comment 4 */ + color: red; /* Comment 5 */ + } +} diff --git a/tests/css_comments/CRLF.less b/tests/css_comments/CRLF.less new file mode 100644 index 00000000..95a8513a --- /dev/null +++ b/tests/css_comments/CRLF.less @@ -0,0 +1,25 @@ +@nice-blue: #5B83AD; +@light-blue: @nice-blue + #111; + +// Comment 1 +/* + * Comment 2 + */ +#header { + // Comment 3 + /* + * Comment 4 + */ + color: @light-blue; +} + +@media only screen and (max-width: 600px) { + // Comment 1 + /* + * Comment 5 + */ + body { + background-color: lightblue; // Comment 6 + color: red; /* Comment 7 */ + } +} diff --git a/tests/css_comments/CRLF.scss b/tests/css_comments/CRLF.scss new file mode 100644 index 00000000..282b582e --- /dev/null +++ b/tests/css_comments/CRLF.scss @@ -0,0 +1,40 @@ +$nice-blue: #5B83AD; +$light-blue: $nice-blue + #111; + +// Comment 1 +/* + * Comment 2 + */ +#header { + // Comment 3 + /* + * Comment 4 + */ + color: @light-blue; +} + +@media only screen and (max-width: 600px) { + // Comment 1 + /* + * Comment 5 + */ + body { + background-color: lightblue; // Comment 6 + color: red; /* Comment 7 */ + } +} + +@mixin create-rules($padding) { + // + // Comment 8 + // + .abc { + padding: $padding; + } + /** + * Comment 9 + */ + .def { + padding: $padding; + } +} diff --git a/tests/css_comments/__snapshots__/jsfmt.spec.js.snap b/tests/css_comments/__snapshots__/jsfmt.spec.js.snap index ad0ef790..8252ee50 100644 --- a/tests/css_comments/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_comments/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,352 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`CRLF.css 1`] = ` +/* + * Comment 1 + */ +#header { + /* + * Comment 2 + */ + color: red; +} + +@media only screen and (max-width: 600px) { + /* + * Comment 3 + */ + body { + background-color: lightblue; /* Comment 4 */ + color: red; /* Comment 5 */ + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* + * Comment 1 + */ +#header { + /* + * Comment 2 + */ + color: red; +} + +@media only screen and (max-width: 600px) { + /* + * Comment 3 + */ + body { + background-color: lightblue; /* Comment 4 */ + color: red; /* Comment 5 */ + } +} + +`; + +exports[`CRLF.less 1`] = ` +@nice-blue: #5B83AD; +@light-blue: @nice-blue + #111; + +// Comment 1 +/* + * Comment 2 + */ +#header { + // Comment 3 + /* + * Comment 4 + */ + color: @light-blue; +} + +@media only screen and (max-width: 600px) { + // Comment 1 + /* + * Comment 5 + */ + body { + background-color: lightblue; // Comment 6 + color: red; /* Comment 7 */ + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@nice-blue: #5B83AD; +@light-blue: @nice-blue + #111; + +// Comment 1 +/* + * Comment 2 + */ +#header { + // Comment 3 + /* + * Comment 4 + */ + color: @light-blue; +} + +@media only screen and (max-width: 600px) { + // Comment 1 + /* + * Comment 5 + */ + body { + background-color: lightblue; // Comment 6 + color: red; /* Comment 7 */ + } +} + +`; + +exports[`CRLF.scss 1`] = ` +$nice-blue: #5B83AD; +$light-blue: $nice-blue + #111; + +// Comment 1 +/* + * Comment 2 + */ +#header { + // Comment 3 + /* + * Comment 4 + */ + color: @light-blue; +} + +@media only screen and (max-width: 600px) { + // Comment 1 + /* + * Comment 5 + */ + body { + background-color: lightblue; // Comment 6 + color: red; /* Comment 7 */ + } +} + +@mixin create-rules($padding) { + // + // Comment 8 + // + .abc { + padding: $padding; + } + /** + * Comment 9 + */ + .def { + padding: $padding; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +$nice-blue: #5b83ad; +$light-blue: $nice-blue + #111; + +// Comment 1 +/* + * Comment 2 + */ +#header { + // Comment 3 + /* + * Comment 4 + */ + color: @light-blue; +} + +@media only screen and (max-width: 600px) { + // Comment 1 + /* + * Comment 5 + */ + body { + background-color: lightblue; // Comment 6 + color: red; /* Comment 7 */ + } +} + +@mixin create-rules($padding) { + // + // Comment 8 + // + .abc { + padding: $padding; + } + /** + * Comment 9 + */ + .def { + padding: $padding; + } +} + +`; + +exports[`at-rules.css 1`] = ` +@charset "utf-8"; /* comment 1 */ + +@import /* comment 2 */ url("fineprint.css") /* comment 3 */ print /* comment 4 */; /* comment 5 */ +@import /* comment 6 */ url("bluish.css") /* comment 7 */ projection /* comment 8 */, /* comment 9 */ tv /* comment 10 */; +/* comment 11 */ @import /* comment 12 */ + /* comment 13 */ url("bluish.css") /* comment 14 */ + /* comment 15 */ projection /* comment 16 */, + /* comment 17 */ tv /* comment 18 */; /* comment 19 */ +/* comment 20 */ +@import/* comment 21 */url("bluish.css")/* comment 22 */projection/* comment 23 */,/* comment 24 */tv/* comment 25 */; +@import /* comment 26 */'custom.css'/* comment 27 */;/* comment 28 */ +@import /* comment 29 */ url('landscape.css') /* comment 30 */ screen /* comment 31 */ and /* comment 32 */ (/* comment 33 */orientation/* comment 34 */:/* comment 35 */landscape/* comment 36 */)/* comment 37 */; + +@namespace /* comment 38 */ url(http://www.w3.org/1999/xhtml) /* comment 39 */ ; +@namespace /* comment 40 */ svg /* comment 41 */ url(http://www.w3.org/2000/svg) /* comment 42 */; + +@keyframes /* comment 43 */ slidein /* comment 44 */ {} + +/* comment 45 */ @font-feature-values /* comment 46 */ Font Two /* comment 47 */ { /* comment 48 */ + /* comment 49 */ @styleset /* comment 50 */ { /* comment 51 */ + nice-style: 4; + /* comment 52 */ } /* comment 53 */ +/* comment 54 */ } /* comment 55 */ + +/* comment 56 */ @counter-style /* comment 57 */ thumbs /* comment 58 */ {} + +/* comment 59 */ @viewport /* comment 60 */ {} + +@page /* comment 61 */ {} +@page /* comment 62 */ :first /* comment 63 */ {} +@page /* comment 64 */ vertical /* comment 65 */ {} + +/* comment 66 */ @media /* comment 67 */ print /* comment 68 */ {} +@media /* comment 69 */ screen /* comment 70 */ , /* comment 71 */ print /* comment 72 */ {} /* comment 73 */ +@media /* comment 74 */ only /* comment 75 */ screen /* comment 76 */ and /* comment 77 */ ( /* comment 78 */ min-width /* comment 79 */ : /* comment 80 */ 320px /* comment 81 */ ) /* comment 82 */ and /* comment 83 */ ( /* comment 84 */ max-width /* comment 85 */ : /* comment 86 */ 480px /* comment 87 */ ) /* comment 88 */ and /* comment 89 */ ( /* comment 90 */ resolution /* comment 91 */ : /* comment 92 */ 150dpi /* comment 93 */ ) /* comment 94 */ {} +@media/* comment 95 */only/* comment 96 */screen/* comment 97 */and/* comment 98 */(/* comment 99 */min-width/* comment 100 */:/* comment 101 */320px/* comment 102 */)/* comment 103 */and/* comment 104 */(/* comment 105 */max-width/* comment 106 */:/* comment 107 */480px/* comment 108 */)/* comment 109 */and/* comment 110 */(/* comment 111 */resolution/* comment 112 */:/* comment 113 */150dpi/* comment 114 */)/* comment 115 */{} +/* comment 116 */@media/* comment 117 */ + /* comment 118 */only/* comment 119 */ + /* comment 120 */screen/* comment 121 */ + /* comment 122 */and/* comment 123 */ + /* comment 124 */(/* comment 125 */ + /* comment 126 */min-width/* comment 127 */ + /* comment 128 */:/* comment 129 */ + /* comment 130 */320px/* comment 131 */ + /* comment 132 */)/* comment 133 */ + /* comment 134 */and/* comment 135 */ + /* comment 136 */(/* comment 137 */ + /* comment 138 */max-width/* comment 139 */ + /* comment 140 */:/* comment 141 */ + /* comment 142 */480px/* comment 143 */ + /* comment 144 */)/* comment 145 */ + /* comment 146 */and/* comment 147 */ + /* comment 148 */(/* comment 149 */ + /* comment 150 */resolution/* comment 151 */ + /* comment 152 */:/* comment 153 */ + /* comment 154 */150dpi/* comment 155 */ + /* comment 156 */)/* comment 157 */ + /* comment 158 */{}/* comment 159 */ + +@supports /* comment 160 */ ( /* comment 161 */ display /* comment 162 */ : /* comment 163 */ flex /* comment 164 */ ) /* comment 165 */ {} +@supports /* comment 166 */ not /* comment 167 */ ( /* comment 168 */ display /* comment 169 */ : /* comment 170 */ flex /* comment 171 */ ) /* comment 172 */ {} +@supports /* comment 173 */ (/* comment 174 */ display /* comment 175 */ : /* comment 176 */ table-cell /* comment 177 */ ) /* comment 178 */ and /* comment 179 */ ( /* comment 180 */ display /* comment 181 */ : /* comment 182 */ list-item /* comment 183 */ ) /* comment 184 */ and /* comment 185 */ ( /* comment 186 */display /* comment 187 */ : /* comment 188 */ run-in /* comment 189 */ ) /* comment 190 */ {} +@supports /* comment 191 */ (/* comment 192 */ --foo /* comment 193 */ : /* comment 194 */ green /* comment 195 */ ) /* comment 196 */ {} + +/* comment 197 */ @supports /* comment 198 */ ( /* comment 199 */ display /* comment 200 */ : /* comment 201 */ flex /* comment 202 */ ) /* comment 203 */ { + /* comment 204 */ @media /* comment 205 */ screen /* comment 206 */ and /* comment 207 */ ( /* comment 208 */ min-width /* comment 209 */ : /* comment 210 */ 900px /* comment 211 */ ) /* comment 212 */ { + /* comment 213 */ } /* comment 214 */ +/* comment 215 */ } /* comment 216 */ + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@charset "utf-8"; /* comment 1 */ + +@import /* comment 2 */ url("fineprint.css") /* comment 3 */ print /* comment 4 */; /* comment 5 */ +@import /* comment 6 */ url("bluish.css") /* comment 7 */ projection /* comment 8 */, + /* comment 9 */ tv /* comment 10 */; +/* comment 11 */ +@import /* comment 12 */ /* comment 13 */ url("bluish.css") /* comment 14 */ /* comment 15 */ projection /* comment 16 */, + /* comment 17 */ tv /* comment 18 */; /* comment 19 */ +/* comment 20 */ +@import /* comment 21 */url("bluish.css") /* comment 22 */projection/* comment 23 */, + /* comment 24 */tv/* comment 25 */; +@import /* comment 26 */"custom.css"/* comment 27 */; /* comment 28 */ +@import /* comment 29 */ url("landscape.css") /* comment 30 */ screen /* comment 31 */ and /* comment 32 */ (/* comment 33 */orientation/* comment 34 */: /* comment 35 */landscape/* comment 36 */) /* comment 37 */; + +@namespace /* comment 38 */ url(http://www.w3.org/1999/xhtml) /* comment 39 */; +@namespace /* comment 40 */ svg /* comment 41 */ url(http://www.w3.org/2000/svg) /* comment 42 */; + +@keyframes /* comment 43 */ slidein /* comment 44 */ { +} + +/* comment 45 */ +@font-feature-values /* comment 46 */ Font Two /* comment 47 */ { + /* comment 48 */ + /* comment 49 */ + @styleset /* comment 50 */ { + /* comment 51 */ + nice-style: 4; + /* comment 52 */ + } /* comment 53 */ + /* comment 54 */ +} /* comment 55 */ + +/* comment 56 */ +@counter-style /* comment 57 */ thumbs /* comment 58 */ { +} + +/* comment 59 */ +@viewport /* comment 60 */ { +} + +@page /* comment 61 */ { +} +@page /* comment 62 */ :first /* comment 63 */ { +} +@page /* comment 64 */ vertical /* comment 65 */ { +} + +/* comment 66 */ +@media /* comment 67 */ print /* comment 68 */ { +} +@media /* comment 69 */ screen /* comment 70 */, + /* comment 71 */ print /* comment 72 */ { +} /* comment 73 */ +@media /* comment 74 */ only /* comment 75 */ screen /* comment 76 */ and /* comment 77 */ (/* comment 78 */ min-width /* comment 79 */: /* comment 80 */ 320px /* comment 81 */) /* comment 82 */ and /* comment 83 */ (/* comment 84 */ max-width /* comment 85 */: /* comment 86 */ 480px /* comment 87 */) /* comment 88 */ and /* comment 89 */ (/* comment 90 */ resolution /* comment 91 */: /* comment 92 */ 150dpi /* comment 93 */) /* comment 94 */ { +} +@media /* comment 95 */only/* comment 96 */screen/* comment 97 */and/* comment 98 */(/* comment 99 */min-width/* comment 100 */:/* comment 101 */320px/* comment 102 */) /* comment 103 */and/* comment 104 */(/* comment 105 */max-width/* comment 106 */:/* comment 107 */480px/* comment 108 */) /* comment 109 */and/* comment 110 */(/* comment 111 */resolution/* comment 112 */:/* comment 113 */150dpi/* comment 114 */) /* comment 115 */ { +} +/* comment 116 */ +@media /* comment 117 */ /* comment 118 */only/* comment 119 */ /* comment 120 */screen/* comment 121 */ /* comment 122 */and/* comment 123 */ /* comment 124 */(/* comment 125 */ + /* comment 126 */min-width/* comment 127 */ + /* comment 128 */:/* comment 129 */ + /* comment 130 */320px/* comment 131 */ + /* comment 132 */) /* comment 133 */ /* comment 134 */and/* comment 135 */ /* comment 136 */(/* comment 137 */ + /* comment 138 */max-width/* comment 139 */ + /* comment 140 */:/* comment 141 */ + /* comment 142 */480px/* comment 143 */ + /* comment 144 */) /* comment 145 */ /* comment 146 */and/* comment 147 */ /* comment 148 */(/* comment 149 */ + /* comment 150 */resolution/* comment 151 */ + /* comment 152 */:/* comment 153 */ + /* comment 154 */150dpi/* comment 155 */ + /* comment 156 */) /* comment 157 */ /* comment 158 */ { +} /* comment 159 */ + +@supports /* comment 160 */ (/* comment 161 */ display /* comment 162 */: /* comment 163 */ flex /* comment 164 */) /* comment 165 */ { +} +@supports /* comment 166 */ not /* comment 167 */ (/* comment 168 */ display /* comment 169 */: /* comment 170 */ flex /* comment 171 */) /* comment 172 */ { +} +@supports /* comment 173 */ (/* comment 174 */ display /* comment 175 */: /* comment 176 */ table-cell /* comment 177 */) /* comment 178 */ and /* comment 179 */ (/* comment 180 */ display /* comment 181 */: /* comment 182 */ list-item /* comment 183 */) /* comment 184 */ and /* comment 185 */ (/* comment 186 */display /* comment 187 */: /* comment 188 */ run-in /* comment 189 */) /* comment 190 */ { +} +@supports /* comment 191 */ (/* comment 192 */ --foo /* comment 193 */: /* comment 194 */ green /* comment 195 */) /* comment 196 */ { +} + +/* comment 197 */ +@supports /* comment 198 */ (/* comment 199 */ display /* comment 200 */: /* comment 201 */ flex /* comment 202 */) /* comment 203 */ { + /* comment 204 */ + @media /* comment 205 */ screen /* comment 206 */ and /* comment 207 */ (/* comment 208 */ min-width /* comment 209 */: /* comment 210 */ 900px /* comment 211 */) /* comment 212 */ { + /* comment 213 */ + } /* comment 214 */ + /* comment 215 */ +} /* comment 216 */ + +`; + exports[`block.css 1`] = ` // Animation definitions cannot cross the shadow boundary, // and thus need to be loaded directly into the atom-text-editor scope. @@ -46,6 +393,227 @@ exports[`bug.css 1`] = ` `; +exports[`declaration.css 1`] = ` +a { + /* comment 1 */ + /* comment 2 */ padding /* comment 3 */ : /* comment 4 */ 10px /* comment 5 */ 10px /* comment 6 */; /* comment 7 */ + /* comment 8 */ + transform: translate(/* comment 9 */ 10px /* comment 10 */); + color: /* comment 11 */ red /* comment 12 */ !important /* comment 13 */ ; /* comment 14 */ + /* comment 15 */ +} + +@font-face { + font-family: "Prettier"; + src: /* comment 16 */ local(/* comment 17 */ "Prettier" /* comment 18 */), /* comment 19 */ + /* comment 20 */ url("http://prettier.com/font.woff") /* comment 21 */ ; /* comment 22 */ +} + +.foo {/* comment 23 */color/* comment 24 */:/* comment 25 */blue/* comment 26 */;/* comment 27 */transform/* comment 28 */:/* comment 29 */translate(/* comment 30 */10px/* comment 31 */)/* comment 32 */;/* comment 33 */} +.foo { /* comment 34 */ color /* comment 35 */ : /* comment 36 */ blue /* comment 37 */ ; /* comment 38 */ transform /* comment 39 */ : /* comment 40 */ translate( /* comment 41 */ 10px /* comment 42 */ ) /* comment 43 */ ; /* comment 44 */ } +.foo { + /* comment 45 */ + /* comment 46 */ color /* comment 47 */ + /* comment 48 */ + /* comment 49 */ : /* comment 50 */ + /* comment 51 */ + /* comment 52 */ blue /* comment 53 */ + /* comment 54 */ + /* comment 55 */ ; /* comment 56 */ + /* comment 57 */ + /* comment 58 */ transform /* comment 59 */ + /* comment 60 */ + /* comment 61 */ : /* comment 62 */ + /* comment 63 */ + /* comment 64 */ translate( /* comment 65 */ + /* comment 66 */ + /* comment 67 */ 10px /* comment 68 */ + /* comment 69 */ + /* comment 70 */ ) /* comment 71 */ + /* comment 72 */ + /* comment 73 */ ; /* comment 74 */ + /* comment 75 */ +} + +body {/* comment 76 */} +body { /* comment 77 */ } +body +{ + /* comment 78 */ +} +body +{ + /* comment 79 */ +} +body +{ + + /* comment 80 */ + +} + +.tab-bar { + .tab {} /* comment 81 */ +} + +.selector { + /* comment 82 */ + position: absolute; + + /* comment 83 */ + display: inline-block; + + /* comment 84 */ + background: #000; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +a { + /* comment 1 */ + /* comment 2 */ + padding/* comment 3 */ : /* comment 4 */ 10px /* comment 5 */ 10px + /* comment 6 */; /* comment 7 */ + /* comment 8 */ + transform: translate(/* comment 9 */ 10px /* comment 10 */); + color: /* comment 11 */ red /* comment 12 */ !important /* comment 13 */ ; /* comment 14 */ + /* comment 15 */ +} + +@font-face { + font-family: "Prettier"; + src: /* comment 16 */ local(/* comment 17 */ "Prettier" /* comment 18 */), + /* comment 19 */ /* comment 20 */ url("http://prettier.com/font.woff") + /* comment 21 */; /* comment 22 */ +} + +.foo { + /* comment 23 */ + color/* comment 24 */:/* comment 25 */ blue /* comment 26 */; /* comment 27 */ + transform/* comment 28 */:/* comment 29 */ translate( + /* comment 30 */ 10px /* comment 31 */ + ) + /* comment 32 */; /* comment 33 */ +} +.foo { + /* comment 34 */ + color/* comment 35 */ : /* comment 36 */ blue /* comment 37 */; /* comment 38 */ + transform/* comment 39 */ : /* comment 40 */ translate( + /* comment 41 */ 10px /* comment 42 */ + ) + /* comment 43 */; /* comment 44 */ +} +.foo { + /* comment 45 */ + /* comment 46 */ + color/* comment 47 */ + /* comment 48 */ + /* comment 49 */ : /* comment 50 */ + /* comment 51 */ + /* comment 52 */ blue + /* comment 53 */ /* comment 54 */ /* comment 55 */; /* comment 56 */ + /* comment 57 */ + /* comment 58 */ + transform/* comment 59 */ + /* comment 60 */ + /* comment 61 */ : /* comment 62 */ + /* comment 63 */ + /* comment 64 */ translate( + /* comment 65 */ /* comment 66 */ /* comment 67 */ 10px /* comment 68 */ + /* comment 69 */ /* comment 70 */ + ) + /* comment 71 */ /* comment 72 */ /* comment 73 */; /* comment 74 */ + /* comment 75 */ +} + +body { + /* comment 76 */ +} +body { + /* comment 77 */ +} +body { + /* comment 78 */ +} +body { + /* comment 79 */ +} +body { + /* comment 80 */ +} + +.tab-bar { + .tab { + } /* comment 81 */ +} + +.selector { + /* comment 82 */ + position: absolute; + + /* comment 83 */ + display: inline-block; + + /* comment 84 */ + background: #000; +} + +`; + +exports[`if-eslit-at-rule-decloration.scss 1`] = ` +@function _normalize-include($section) { +// Check if $section is in the $include list. + @if index($_normalize-include, $section) { + @return true; + } +// If $include is set to (all), make sure $section is not in $exclude. + @else if not index($_normalize-exclude, $section) and index($_normalize-include, all) { + @return true; + } + @return false; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@function _normalize-include($section) { + // Check if $section is in the $include list. + @if index($_normalize-include, $section) { + @return true; + } + // If $include is set to (all), make sure $section is not in $exclude. + @else if not + index($_normalize-exclude, $section) and + index($_normalize-include, all) + { + @return true; + } + @return false; +} + +`; + +exports[`lists.scss 1`] = ` +$my-list: + 'foo', // Foo + 'bar'; // Bar +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +$my-list: "foo", + // Foo + "bar"; // Bar + +`; + +exports[`maps.scss 1`] = ` +$my-map: ( + 'foo': 1, // Foo + 'bar': 2, // Bar +); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +$my-map: ( + "foo": 1, + // Foo + "bar": 2, + // Bar +); + +`; + exports[`places.css 1`] = ` div { // a @@ -91,32 +659,549 @@ foo { `; -exports[`selector.css 1`] = ` -.powerPathNavigator .helm button.pressedButton, /* pressed buttons */ +exports[`selectors.css 1`] = ` +.powerPathNavigator .helm button.pressedButton, /* comment 1 */ .powerPathNavigator .helm button:active:not(.disabledButton), .powerPathNavigator table.powerPathInfo th:active, .powerPathNavigator table.powerPathInfo th:active + th:last-child { } -/* pressed buttons */ +/* comment 2 */ .powerPathNavigator .helm button.pressedButton, .powerPathNavigator .helm button:active:not(.disabledButton) { } + +.foo, +/* comment 3 */ +.bar { + display: block; +} + +/* comment 4 */ +/* comment 5 */ .field /* comment 6 */ +/* comment 7 */ +/* comment 8 */ { /* comment 9 */ + /* comment 10 */ + background: green; + /* comment 11 */ +/* comment 12 */ } /* comment 13 */ +/* comment 14 */ + +/* comment 15 */ .element /* comment 16 */ , /* comment 17 */ .element /* comment 18 */ {} +/* comment 19 */ +.element +/* comment 20 */ +, +/* comment 21 */ +.element +/* comment 22 */ +{} + +/* comment 23 */ .element /* comment 24 */ .element /* comment 25 */ {} +/* comment 26 */ +.element +/* comment 27 */ +.element +/* comment 28 */ +{} + +/* comment 29 */ .element /* comment 30 */ > /* comment 31 */ .element /* comment 32 */ {} +/* comment 33 */ +.element +/* comment 34 */ +> +/* comment 35 */ +.element +/* comment 36 */ +{} + +/* comment 37 */ .element /* comment 38 */ + /* comment 39 */ .element /* comment 40 */ {} +/* comment 41 */ +.element +/* comment 42 */ ++ +/* comment 43 */ +.element +/* comment 44 */ +{} + +/* comment 45 */ .element /* comment 46 */ ~ /* comment 47 */ .element /* comment 48 */ {} +/* comment 49 */ +.element +/* comment 50 */ +~ +/* comment 51 */ +.element +/* comment 52 */ +{} + +/* comment 53 */ .element /* comment 54 */ >>> /* comment 55 */ .element /* comment 56 */ {} +/* comment 57 */ +.element +/* comment 58 */ +>>> +/* comment 59 */ +.element +/* comment 60 */ +{} + +a[/* comment 61 */target/* comment 62 */] {} +a[/* comment 63 */target/* comment 64 */=/* comment 65 */"_blank"/* comment 66 */] {} +a[/* comment 67 */target/* comment 68 */~=/* comment 69 */"_blank"/* comment 70 */] {} +a[/* comment 71 */target/* comment 72 */|=/* comment 73 */"_blank"/* comment 74 */] {} +a[/* comment 75 */target/* comment 76 */^=/* comment 77 */"_blank"/* comment 78 */] {} +a[/* comment 79 */target/* comment 80 */$=/* comment 81 */"_blank"/* comment 82 */] {} +a[/* comment 83 */target/* comment 84 */*=/* comment 85 */"_blank"/* comment 86 */] {} + +[/* comment 87 */foo/* comment 88 */|/* comment 89 */att/* comment 90 */=/* comment 91 */val/* comment 92 */] {} +[/* comment 93 */*/* comment 94 */|/* comment 95 */att/* comment 96 */] {} +[/* comment 97 */|/* comment 98 */att/* comment 99 */] {} + +a/* comment 100 */:/* comment 101 */active {} + +a/* comment 102 */::/* comment 103 */after {} + +p:lang(/* comment 104 */it/* comment 105 */) {} + +span:nth-child(/* comment 106 */2n/* comment 107 */+/* comment 108 */1/* comment 109 */) {} + +/* comment 110 */::-webkit-progress-bar {} + +/* comment 111 */:matches(/* comment 112 */section/* comment 113 */,/* comment 114 */ article/* comment 115 */) /* comment 116 */ h1 {} + +/* comment 117 */*/* comment 118 */ {} + +/* comment 119 */#container/* comment 120 */{} + +/* comment 121 */.error/* comment 122 */{} + +/* comment 123 */a/* comment 124 */{} + +input:not(/* comment 125 */[/* comment 126 */disabled/* comment 127 */]/* comment 128 */) {} + +/* comment 129 */h1/* comment 130 */, /* comment 131 */h2/* comment 132 */,/* comment 133 */ h3/* comment 134 */ {} + +/* comment 135 */.phone/* comment 136 */ { /* comment 137 */ + /* comment 138 */&_title/* comment 139 */ { /* comment 140 */ + width: 500px; + /* comment 141 */body.is_dark/* comment 142 */ & /* comment 143 */{ /* comment 144 */ + color: white; + /* comment 145 */ } /* comment 146 */ + /* comment 147 */ } /* comment 148 */ + + /* comment 149 */ img/* comment 150 */ { /* comment 151 */ + display: block; + /* comment 152 */ } /* comment 153 */ +/* comment 154 */ +} /* comment 155 */ + +@media (max-width: 500px) { + /* comment 156 */ a /* comment 157 */ { /* comment 158 */ + /* comment 159 */b/* comment 160 */ {/* comment 161 */} /* comment 162 */ + /* comment 163 */ } /* comment 164 */ +/* comment 165 */ } /* comment 166 */ + +/* comment 167 */ article /* comment 168 */ :--heading /* comment 169 */ + /* comment 170 */ p /* comment 171 */ {} + +/* comment 172 */ .foo /* comment 173 */ :global /* comment 174 */.bar /* comment 175 */{} +/* comment 176 */ .foo /* comment 177 */ :global(/* comment 178 */.bar/* comment 179 */) /* comment 180 */ .baz /* comment 181 */ {} + +/* comment 182 */ .foo /* comment 183 */ :local /* comment 184 */ .bar /* comment 185 */ {} +/* comment 186 */ .foo /* comment 187 */ :local(/* comment 188 */.foo/* comment 189 */) /* comment 190 */ .bar /* comment 191 */ {} + +/* custom properties set & @apply rule */ +:root { + /* comments 192 */ --centered /* comments 193 */ : /* comments 194 */ { + display: flex; + align-items: center; + justify-content: center; + }; +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -.powerPathNavigator .helm button.pressedButton, -/* pressed buttons */ - .powerPathNavigator - .helm - button:active:not(.disabledButton), +.powerPathNavigator .helm button.pressedButton, /* comment 1 */ +.powerPathNavigator .helm button:active:not(.disabledButton), .powerPathNavigator table.powerPathInfo th:active, .powerPathNavigator table.powerPathInfo th:active + th:last-child { } -/* pressed buttons */ +/* comment 2 */ .powerPathNavigator .helm button.pressedButton, .powerPathNavigator .helm button:active:not(.disabledButton) { } +.foo, +/* comment 3 */ +.bar { + display: block; +} + +/* comment 4 */ +/* comment 5 */ +.field /* comment 6 */ +/* comment 7 */ +/* comment 8 */ { + /* comment 9 */ + /* comment 10 */ + background: green; + /* comment 11 */ + /* comment 12 */ +} /* comment 13 */ +/* comment 14 */ + +/* comment 15 */ +.element /* comment 16 */ , /* comment 17 */ .element /* comment 18 */ { +} +/* comment 19 */ +.element +/* comment 20 */ +, +/* comment 21 */ +.element +/* comment 22 */ + { +} + +/* comment 23 */ +.element /* comment 24 */ .element /* comment 25 */ { +} +/* comment 26 */ +.element +/* comment 27 */ +.element +/* comment 28 */ + { +} + +/* comment 29 */ +.element /* comment 30 */ > /* comment 31 */ .element /* comment 32 */ { +} +/* comment 33 */ +.element +/* comment 34 */ +> +/* comment 35 */ +.element +/* comment 36 */ + { +} + +/* comment 37 */ +.element /* comment 38 */ + /* comment 39 */ .element /* comment 40 */ { +} +/* comment 41 */ +.element +/* comment 42 */ ++ +/* comment 43 */ +.element +/* comment 44 */ + { +} + +/* comment 45 */ +.element /* comment 46 */ ~ /* comment 47 */ .element /* comment 48 */ { +} +/* comment 49 */ +.element +/* comment 50 */ +~ +/* comment 51 */ +.element +/* comment 52 */ + { +} + +/* comment 53 */ +.element /* comment 54 */ >>> /* comment 55 */ .element /* comment 56 */ { +} +/* comment 57 */ +.element +/* comment 58 */ +>>> +/* comment 59 */ +.element +/* comment 60 */ + { +} + +a[/* comment 61 */target/* comment 62 */] { +} +a[/* comment 63 */target/* comment 64 */=/* comment 65 */"_blank"/* comment 66 */] { +} +a[/* comment 67 */target/* comment 68 */~=/* comment 69 */"_blank"/* comment 70 */] { +} +a[/* comment 71 */target/* comment 72 */|=/* comment 73 */"_blank"/* comment 74 */] { +} +a[/* comment 75 */target/* comment 76 */^=/* comment 77 */"_blank"/* comment 78 */] { +} +a[/* comment 79 */target/* comment 80 */$=/* comment 81 */"_blank"/* comment 82 */] { +} +a[/* comment 83 */target/* comment 84 */*=/* comment 85 */"_blank"/* comment 86 */] { +} + +[/* comment 87 */foo/* comment 88 */|/* comment 89 */att/* comment 90 */=/* comment 91 */val/* comment 92 */] { +} +[/* comment 93 */*/* comment 94 */|/* comment 95 */att/* comment 96 */] { +} +[/* comment 97 */|/* comment 98 */att/* comment 99 */] { +} + +a/* comment 100 */:/* comment 101 */active { +} + +a/* comment 102 */::/* comment 103 */after { +} + +p:lang(/* comment 104 */it/* comment 105 */) { +} + +span:nth-child(/* comment 106 */2n/* comment 107 */+/* comment 108 */1/* comment 109 */) { +} + +/* comment 110 */ +::-webkit-progress-bar { +} + +/* comment 111 */ +:matches(/* comment 112 */section/* comment 113 */,/* comment 114 */ article/* comment 115 */) /* comment 116 */ h1 { +} + +/* comment 117 */ +*/* comment 118 */ { +} + +/* comment 119 */ +#container/* comment 120 */ { +} + +/* comment 121 */ +.error/* comment 122 */ { +} + +/* comment 123 */ +a/* comment 124 */ { +} + +input:not(/* comment 125 */[/* comment 126 */disabled/* comment 127 */]/* comment 128 */) { +} + +/* comment 129 */ +h1/* comment 130 */, /* comment 131 */h2/* comment 132 */,/* comment 133 */ h3/* comment 134 */ { +} + +/* comment 135 */ +.phone/* comment 136 */ { + /* comment 137 */ + /* comment 138 */ + &_title/* comment 139 */ { + /* comment 140 */ + width: 500px; + /* comment 141 */ + body.is_dark/* comment 142 */ & /* comment 143 */ { + /* comment 144 */ + color: white; + /* comment 145 */ + } /* comment 146 */ + /* comment 147 */ + } /* comment 148 */ + + /* comment 149 */ + img/* comment 150 */ { + /* comment 151 */ + display: block; + /* comment 152 */ + } /* comment 153 */ + /* comment 154 */ +} /* comment 155 */ + +@media (max-width: 500px) { + /* comment 156 */ + a /* comment 157 */ { + /* comment 158 */ + /* comment 159 */ + b/* comment 160 */ { + /* comment 161 */ + } /* comment 162 */ + /* comment 163 */ + } /* comment 164 */ + /* comment 165 */ +} /* comment 166 */ + +/* comment 167 */ +article /* comment 168 */ :--heading /* comment 169 */ + /* comment 170 */ p /* comment 171 */ { +} + +/* comment 172 */ +.foo /* comment 173 */ :global /* comment 174 */.bar /* comment 175 */ { +} +/* comment 176 */ +.foo /* comment 177 */ :global(/* comment 178 */.bar/* comment 179 */) /* comment 180 */ .baz /* comment 181 */ { +} + +/* comment 182 */ +.foo /* comment 183 */ :local /* comment 184 */ .bar /* comment 185 */ { +} +/* comment 186 */ +.foo /* comment 187 */ :local(/* comment 188 */.foo/* comment 189 */) /* comment 190 */ .bar /* comment 191 */ { +} + +/* custom properties set & @apply rule */ +:root { + /* comments 192 */ + --centered /* comments 193 */ : /* comments 194 */ { + display: flex; + align-items: center; + justify-content: center; + } +} + +`; + +exports[`selectors.scss 1`] = ` +.powerPathNavigator .helm button.pressedButton, // comment 1 +.powerPathNavigator .helm button:active:not(.disabledButton), +.powerPathNavigator table.powerPathInfo th:active, +.powerPathNavigator table.powerPathInfo th:active + th:last-child { +} + +// comment 2 +.powerPathNavigator .helm button.pressedButton, +.powerPathNavigator .helm button:active:not(.disabledButton) { +} + +.foo, +// comment 3 +.bar { + display: block; +} + +.field + { + &[data-field-id="1"], // comment 4 + &[data-field-id="2"], // comment 5 + &[data-field-id="3"], // comment 6 + { + background: green; + } +} + +// comment 7 +.field +// comment 8 +{ // comment 9 + // comment 10 + &[data-field-id="1"], // comment 11 + // comment 12 + &[data-field-id="2"] // comment 13 + // comment 14 + , // comment 15 + // comment 16 + &[data-field-id="3"], // comment 17 + // comment 18 + { // comment 19 + // comment 20 + background: green; + // comment 21 + } // comment 22 + // comment 23 +} +// comment 24 + +.foo +// comment 25 +.bar +// comment 26 +{} + +.foo +// comment 27 ++ +// comment 28 +.bar +// comment 29 +{} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.powerPathNavigator .helm button.pressedButton, // comment 1 +.powerPathNavigator .helm button:active:not(.disabledButton), +.powerPathNavigator table.powerPathInfo th:active, +.powerPathNavigator table.powerPathInfo th:active + th:last-child { +} + +// comment 2 +.powerPathNavigator .helm button.pressedButton, +.powerPathNavigator .helm button:active:not(.disabledButton) { +} + +.foo, +// comment 3 +.bar { + display: block; +} + +.field { + &[data-field-id="1"], // comment 4 + &[data-field-id="2"], // comment 5 + &[data-field-id="3"], // comment 6 + { + background: green; + } +} + +// comment 7 +.field +// comment 8 + { + // comment 9 + // comment 10 + &[data-field-id="1"], // comment 11 + // comment 12 + &[data-field-id="2"] // comment 13 + // comment 14 + , // comment 15 + // comment 16 + &[data-field-id="3"], // comment 17 + // comment 18 + { + // comment 19 + // comment 20 + background: green; + // comment 21 + } // comment 22 + // comment 23 +} +// comment 24 + +.foo +// comment 25 +.bar +// comment 26 + { +} + +.foo +// comment 27 ++ +// comment 28 +.bar +// comment 29 + { +} + +`; + +exports[`source-map.css 1`] = ` +.foo { + color: red; +} + +/*# sourceMappingURL=file.css.map */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.foo { + color: red; +} + +/*# sourceMappingURL=file.css.map */ + `; exports[`trailing_star_slash.css 1`] = ` @@ -132,3 +1217,133 @@ a { } `; + +exports[`types.css 1`] = ` +/* This is a basic comment */ + +/* ========================================================================== + This is a Section Comment + ========================================================================== */ + +/* + This is a sub-section comment + ========================================================================== */ + +/** + * Short description using Doxygen-style comment format + * + * The first sentence of the long description starts here and continues on this + * line for a while finally concluding here at the end of this paragraph. + * + * The long description is ideal for more detailed explanations and + * documentation. It can include example HTML, URLs, or any other information + * that is deemed necessary or useful. + * + * @tag This is a tag named 'tag' + * + * TODO: This is a todo statement that describes an atomic task to be completed + * at a later date. It wraps after 80 characters and following lines are + * indented by 2 spaces. + */ + +/** + * TODO: + * + * => Write some code + * => Make some lists + * + */ + +/* +span { + color: blue; + font-size: 1.5em; +} +*/ + +/* +Lorem ipsum dolor +*/ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/* This is a basic comment */ + +/* ========================================================================== + This is a Section Comment + ========================================================================== */ + +/* + This is a sub-section comment + ========================================================================== */ + +/** + * Short description using Doxygen-style comment format + * + * The first sentence of the long description starts here and continues on this + * line for a while finally concluding here at the end of this paragraph. + * + * The long description is ideal for more detailed explanations and + * documentation. It can include example HTML, URLs, or any other information + * that is deemed necessary or useful. + * + * @tag This is a tag named 'tag' + * + * TODO: This is a todo statement that describes an atomic task to be completed + * at a later date. It wraps after 80 characters and following lines are + * indented by 2 spaces. + */ + +/** + * TODO: + * + * => Write some code + * => Make some lists + * + */ + +/* +span { + color: blue; + font-size: 1.5em; +} +*/ + +/* +Lorem ipsum dolor +*/ + +`; + +exports[`variable-declaration.scss 1`] = ` +$var: /* comment 1 */ all /* comment 2 */ !default /* comment 3 */ ; /* comment 4 */ + +@mixin text-color { + /* comment 5 */ + /* comment 6 */ $text-color /* comment 7 */ : /* comment 8 */ red /* comment 9 */ !default /* comment 10 */ ; /* comment 11 */ + /* comment 12 */ + color: $text-color; +} + +.error { + /* comment 13 */ + /* comment 14 */ $text-color /* comment 15 */ : /* comment 16 */ green /* comment 17 */ !global /* comment 18 */ ; /* comment 19 */ + /* comment 20 */ +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +$var: /* comment 1 */ all /* comment 2 */ !default /* comment 3 */ ; /* comment 4 */ + +@mixin text-color { + /* comment 5 */ + /* comment 6 */ + $text-color/* comment 7 */ : /* comment 8 */ red /* comment 9 */ !default /* comment 10 */ ; /* comment 11 */ + /* comment 12 */ + color: $text-color; +} + +.error { + /* comment 13 */ + /* comment 14 */ + $text-color/* comment 15 */ : /* comment 16 */ green /* comment 17 */ !global /* comment 18 */ ; /* comment 19 */ + /* comment 20 */ +} + +`; diff --git a/tests/css_comments/at-rules.css b/tests/css_comments/at-rules.css new file mode 100644 index 00000000..e1d0a229 --- /dev/null +++ b/tests/css_comments/at-rules.css @@ -0,0 +1,69 @@ +@charset "utf-8"; /* comment 1 */ + +@import /* comment 2 */ url("fineprint.css") /* comment 3 */ print /* comment 4 */; /* comment 5 */ +@import /* comment 6 */ url("bluish.css") /* comment 7 */ projection /* comment 8 */, /* comment 9 */ tv /* comment 10 */; +/* comment 11 */ @import /* comment 12 */ + /* comment 13 */ url("bluish.css") /* comment 14 */ + /* comment 15 */ projection /* comment 16 */, + /* comment 17 */ tv /* comment 18 */; /* comment 19 */ +/* comment 20 */ +@import/* comment 21 */url("bluish.css")/* comment 22 */projection/* comment 23 */,/* comment 24 */tv/* comment 25 */; +@import /* comment 26 */'custom.css'/* comment 27 */;/* comment 28 */ +@import /* comment 29 */ url('landscape.css') /* comment 30 */ screen /* comment 31 */ and /* comment 32 */ (/* comment 33 */orientation/* comment 34 */:/* comment 35 */landscape/* comment 36 */)/* comment 37 */; + +@namespace /* comment 38 */ url(http://www.w3.org/1999/xhtml) /* comment 39 */ ; +@namespace /* comment 40 */ svg /* comment 41 */ url(http://www.w3.org/2000/svg) /* comment 42 */; + +@keyframes /* comment 43 */ slidein /* comment 44 */ {} + +/* comment 45 */ @font-feature-values /* comment 46 */ Font Two /* comment 47 */ { /* comment 48 */ + /* comment 49 */ @styleset /* comment 50 */ { /* comment 51 */ + nice-style: 4; + /* comment 52 */ } /* comment 53 */ +/* comment 54 */ } /* comment 55 */ + +/* comment 56 */ @counter-style /* comment 57 */ thumbs /* comment 58 */ {} + +/* comment 59 */ @viewport /* comment 60 */ {} + +@page /* comment 61 */ {} +@page /* comment 62 */ :first /* comment 63 */ {} +@page /* comment 64 */ vertical /* comment 65 */ {} + +/* comment 66 */ @media /* comment 67 */ print /* comment 68 */ {} +@media /* comment 69 */ screen /* comment 70 */ , /* comment 71 */ print /* comment 72 */ {} /* comment 73 */ +@media /* comment 74 */ only /* comment 75 */ screen /* comment 76 */ and /* comment 77 */ ( /* comment 78 */ min-width /* comment 79 */ : /* comment 80 */ 320px /* comment 81 */ ) /* comment 82 */ and /* comment 83 */ ( /* comment 84 */ max-width /* comment 85 */ : /* comment 86 */ 480px /* comment 87 */ ) /* comment 88 */ and /* comment 89 */ ( /* comment 90 */ resolution /* comment 91 */ : /* comment 92 */ 150dpi /* comment 93 */ ) /* comment 94 */ {} +@media/* comment 95 */only/* comment 96 */screen/* comment 97 */and/* comment 98 */(/* comment 99 */min-width/* comment 100 */:/* comment 101 */320px/* comment 102 */)/* comment 103 */and/* comment 104 */(/* comment 105 */max-width/* comment 106 */:/* comment 107 */480px/* comment 108 */)/* comment 109 */and/* comment 110 */(/* comment 111 */resolution/* comment 112 */:/* comment 113 */150dpi/* comment 114 */)/* comment 115 */{} +/* comment 116 */@media/* comment 117 */ + /* comment 118 */only/* comment 119 */ + /* comment 120 */screen/* comment 121 */ + /* comment 122 */and/* comment 123 */ + /* comment 124 */(/* comment 125 */ + /* comment 126 */min-width/* comment 127 */ + /* comment 128 */:/* comment 129 */ + /* comment 130 */320px/* comment 131 */ + /* comment 132 */)/* comment 133 */ + /* comment 134 */and/* comment 135 */ + /* comment 136 */(/* comment 137 */ + /* comment 138 */max-width/* comment 139 */ + /* comment 140 */:/* comment 141 */ + /* comment 142 */480px/* comment 143 */ + /* comment 144 */)/* comment 145 */ + /* comment 146 */and/* comment 147 */ + /* comment 148 */(/* comment 149 */ + /* comment 150 */resolution/* comment 151 */ + /* comment 152 */:/* comment 153 */ + /* comment 154 */150dpi/* comment 155 */ + /* comment 156 */)/* comment 157 */ + /* comment 158 */{}/* comment 159 */ + +@supports /* comment 160 */ ( /* comment 161 */ display /* comment 162 */ : /* comment 163 */ flex /* comment 164 */ ) /* comment 165 */ {} +@supports /* comment 166 */ not /* comment 167 */ ( /* comment 168 */ display /* comment 169 */ : /* comment 170 */ flex /* comment 171 */ ) /* comment 172 */ {} +@supports /* comment 173 */ (/* comment 174 */ display /* comment 175 */ : /* comment 176 */ table-cell /* comment 177 */ ) /* comment 178 */ and /* comment 179 */ ( /* comment 180 */ display /* comment 181 */ : /* comment 182 */ list-item /* comment 183 */ ) /* comment 184 */ and /* comment 185 */ ( /* comment 186 */display /* comment 187 */ : /* comment 188 */ run-in /* comment 189 */ ) /* comment 190 */ {} +@supports /* comment 191 */ (/* comment 192 */ --foo /* comment 193 */ : /* comment 194 */ green /* comment 195 */ ) /* comment 196 */ {} + +/* comment 197 */ @supports /* comment 198 */ ( /* comment 199 */ display /* comment 200 */ : /* comment 201 */ flex /* comment 202 */ ) /* comment 203 */ { + /* comment 204 */ @media /* comment 205 */ screen /* comment 206 */ and /* comment 207 */ ( /* comment 208 */ min-width /* comment 209 */ : /* comment 210 */ 900px /* comment 211 */ ) /* comment 212 */ { + /* comment 213 */ } /* comment 214 */ +/* comment 215 */ } /* comment 216 */ + diff --git a/tests/css_comments/declaration.css b/tests/css_comments/declaration.css new file mode 100644 index 00000000..3d34b0b0 --- /dev/null +++ b/tests/css_comments/declaration.css @@ -0,0 +1,72 @@ +a { + /* comment 1 */ + /* comment 2 */ padding /* comment 3 */ : /* comment 4 */ 10px /* comment 5 */ 10px /* comment 6 */; /* comment 7 */ + /* comment 8 */ + transform: translate(/* comment 9 */ 10px /* comment 10 */); + color: /* comment 11 */ red /* comment 12 */ !important /* comment 13 */ ; /* comment 14 */ + /* comment 15 */ +} + +@font-face { + font-family: "Prettier"; + src: /* comment 16 */ local(/* comment 17 */ "Prettier" /* comment 18 */), /* comment 19 */ + /* comment 20 */ url("http://prettier.com/font.woff") /* comment 21 */ ; /* comment 22 */ +} + +.foo {/* comment 23 */color/* comment 24 */:/* comment 25 */blue/* comment 26 */;/* comment 27 */transform/* comment 28 */:/* comment 29 */translate(/* comment 30 */10px/* comment 31 */)/* comment 32 */;/* comment 33 */} +.foo { /* comment 34 */ color /* comment 35 */ : /* comment 36 */ blue /* comment 37 */ ; /* comment 38 */ transform /* comment 39 */ : /* comment 40 */ translate( /* comment 41 */ 10px /* comment 42 */ ) /* comment 43 */ ; /* comment 44 */ } +.foo { + /* comment 45 */ + /* comment 46 */ color /* comment 47 */ + /* comment 48 */ + /* comment 49 */ : /* comment 50 */ + /* comment 51 */ + /* comment 52 */ blue /* comment 53 */ + /* comment 54 */ + /* comment 55 */ ; /* comment 56 */ + /* comment 57 */ + /* comment 58 */ transform /* comment 59 */ + /* comment 60 */ + /* comment 61 */ : /* comment 62 */ + /* comment 63 */ + /* comment 64 */ translate( /* comment 65 */ + /* comment 66 */ + /* comment 67 */ 10px /* comment 68 */ + /* comment 69 */ + /* comment 70 */ ) /* comment 71 */ + /* comment 72 */ + /* comment 73 */ ; /* comment 74 */ + /* comment 75 */ +} + +body {/* comment 76 */} +body { /* comment 77 */ } +body +{ + /* comment 78 */ +} +body +{ + /* comment 79 */ +} +body +{ + + /* comment 80 */ + +} + +.tab-bar { + .tab {} /* comment 81 */ +} + +.selector { + /* comment 82 */ + position: absolute; + + /* comment 83 */ + display: inline-block; + + /* comment 84 */ + background: #000; +} diff --git a/tests/css_comment_in_if/comment-in-if.css b/tests/css_comments/if-eslit-at-rule-decloration.scss similarity index 53% rename from tests/css_comment_in_if/comment-in-if.css rename to tests/css_comments/if-eslit-at-rule-decloration.scss index ccb4a9c6..3542ceca 100644 --- a/tests/css_comment_in_if/comment-in-if.css +++ b/tests/css_comments/if-eslit-at-rule-decloration.scss @@ -1,11 +1,11 @@ @function _normalize-include($section) { // Check if $section is in the $include list. - @if index($_normalize-include, $section) { + @if index($_normalize-include, $section) { @return true; - } + } // If $include is set to (all), make sure $section is not in $exclude. - @else if not index($_normalize-exclude, $section) and index($_normalize-include, all) { + @else if not index($_normalize-exclude, $section) and index($_normalize-include, all) { @return true; - } - @return false; + } + @return false; } diff --git a/tests/css_comments/lists.scss b/tests/css_comments/lists.scss new file mode 100644 index 00000000..7e2d9b3c --- /dev/null +++ b/tests/css_comments/lists.scss @@ -0,0 +1,3 @@ +$my-list: + 'foo', // Foo + 'bar'; // Bar diff --git a/tests/css_comments/maps.scss b/tests/css_comments/maps.scss new file mode 100644 index 00000000..0939fa59 --- /dev/null +++ b/tests/css_comments/maps.scss @@ -0,0 +1,4 @@ +$my-map: ( + 'foo': 1, // Foo + 'bar': 2, // Bar +); diff --git a/tests/css_comments/selector.css b/tests/css_comments/selector.css deleted file mode 100644 index 53bdc145..00000000 --- a/tests/css_comments/selector.css +++ /dev/null @@ -1,10 +0,0 @@ -.powerPathNavigator .helm button.pressedButton, /* pressed buttons */ -.powerPathNavigator .helm button:active:not(.disabledButton), -.powerPathNavigator table.powerPathInfo th:active, -.powerPathNavigator table.powerPathInfo th:active + th:last-child { -} - -/* pressed buttons */ -.powerPathNavigator .helm button.pressedButton, -.powerPathNavigator .helm button:active:not(.disabledButton) { -} diff --git a/tests/css_comments/selectors.css b/tests/css_comments/selectors.css new file mode 100644 index 00000000..fb505059 --- /dev/null +++ b/tests/css_comments/selectors.css @@ -0,0 +1,157 @@ +.powerPathNavigator .helm button.pressedButton, /* comment 1 */ +.powerPathNavigator .helm button:active:not(.disabledButton), +.powerPathNavigator table.powerPathInfo th:active, +.powerPathNavigator table.powerPathInfo th:active + th:last-child { +} + +/* comment 2 */ +.powerPathNavigator .helm button.pressedButton, +.powerPathNavigator .helm button:active:not(.disabledButton) { +} + +.foo, +/* comment 3 */ +.bar { + display: block; +} + +/* comment 4 */ +/* comment 5 */ .field /* comment 6 */ +/* comment 7 */ +/* comment 8 */ { /* comment 9 */ + /* comment 10 */ + background: green; + /* comment 11 */ +/* comment 12 */ } /* comment 13 */ +/* comment 14 */ + +/* comment 15 */ .element /* comment 16 */ , /* comment 17 */ .element /* comment 18 */ {} +/* comment 19 */ +.element +/* comment 20 */ +, +/* comment 21 */ +.element +/* comment 22 */ +{} + +/* comment 23 */ .element /* comment 24 */ .element /* comment 25 */ {} +/* comment 26 */ +.element +/* comment 27 */ +.element +/* comment 28 */ +{} + +/* comment 29 */ .element /* comment 30 */ > /* comment 31 */ .element /* comment 32 */ {} +/* comment 33 */ +.element +/* comment 34 */ +> +/* comment 35 */ +.element +/* comment 36 */ +{} + +/* comment 37 */ .element /* comment 38 */ + /* comment 39 */ .element /* comment 40 */ {} +/* comment 41 */ +.element +/* comment 42 */ ++ +/* comment 43 */ +.element +/* comment 44 */ +{} + +/* comment 45 */ .element /* comment 46 */ ~ /* comment 47 */ .element /* comment 48 */ {} +/* comment 49 */ +.element +/* comment 50 */ +~ +/* comment 51 */ +.element +/* comment 52 */ +{} + +/* comment 53 */ .element /* comment 54 */ >>> /* comment 55 */ .element /* comment 56 */ {} +/* comment 57 */ +.element +/* comment 58 */ +>>> +/* comment 59 */ +.element +/* comment 60 */ +{} + +a[/* comment 61 */target/* comment 62 */] {} +a[/* comment 63 */target/* comment 64 */=/* comment 65 */"_blank"/* comment 66 */] {} +a[/* comment 67 */target/* comment 68 */~=/* comment 69 */"_blank"/* comment 70 */] {} +a[/* comment 71 */target/* comment 72 */|=/* comment 73 */"_blank"/* comment 74 */] {} +a[/* comment 75 */target/* comment 76 */^=/* comment 77 */"_blank"/* comment 78 */] {} +a[/* comment 79 */target/* comment 80 */$=/* comment 81 */"_blank"/* comment 82 */] {} +a[/* comment 83 */target/* comment 84 */*=/* comment 85 */"_blank"/* comment 86 */] {} + +[/* comment 87 */foo/* comment 88 */|/* comment 89 */att/* comment 90 */=/* comment 91 */val/* comment 92 */] {} +[/* comment 93 */*/* comment 94 */|/* comment 95 */att/* comment 96 */] {} +[/* comment 97 */|/* comment 98 */att/* comment 99 */] {} + +a/* comment 100 */:/* comment 101 */active {} + +a/* comment 102 */::/* comment 103 */after {} + +p:lang(/* comment 104 */it/* comment 105 */) {} + +span:nth-child(/* comment 106 */2n/* comment 107 */+/* comment 108 */1/* comment 109 */) {} + +/* comment 110 */::-webkit-progress-bar {} + +/* comment 111 */:matches(/* comment 112 */section/* comment 113 */,/* comment 114 */ article/* comment 115 */) /* comment 116 */ h1 {} + +/* comment 117 */*/* comment 118 */ {} + +/* comment 119 */#container/* comment 120 */{} + +/* comment 121 */.error/* comment 122 */{} + +/* comment 123 */a/* comment 124 */{} + +input:not(/* comment 125 */[/* comment 126 */disabled/* comment 127 */]/* comment 128 */) {} + +/* comment 129 */h1/* comment 130 */, /* comment 131 */h2/* comment 132 */,/* comment 133 */ h3/* comment 134 */ {} + +/* comment 135 */.phone/* comment 136 */ { /* comment 137 */ + /* comment 138 */&_title/* comment 139 */ { /* comment 140 */ + width: 500px; + /* comment 141 */body.is_dark/* comment 142 */ & /* comment 143 */{ /* comment 144 */ + color: white; + /* comment 145 */ } /* comment 146 */ + /* comment 147 */ } /* comment 148 */ + + /* comment 149 */ img/* comment 150 */ { /* comment 151 */ + display: block; + /* comment 152 */ } /* comment 153 */ +/* comment 154 */ +} /* comment 155 */ + +@media (max-width: 500px) { + /* comment 156 */ a /* comment 157 */ { /* comment 158 */ + /* comment 159 */b/* comment 160 */ {/* comment 161 */} /* comment 162 */ + /* comment 163 */ } /* comment 164 */ +/* comment 165 */ } /* comment 166 */ + +/* comment 167 */ article /* comment 168 */ :--heading /* comment 169 */ + /* comment 170 */ p /* comment 171 */ {} + +/* comment 172 */ .foo /* comment 173 */ :global /* comment 174 */.bar /* comment 175 */{} +/* comment 176 */ .foo /* comment 177 */ :global(/* comment 178 */.bar/* comment 179 */) /* comment 180 */ .baz /* comment 181 */ {} + +/* comment 182 */ .foo /* comment 183 */ :local /* comment 184 */ .bar /* comment 185 */ {} +/* comment 186 */ .foo /* comment 187 */ :local(/* comment 188 */.foo/* comment 189 */) /* comment 190 */ .bar /* comment 191 */ {} + +/* custom properties set & @apply rule */ +:root { + /* comments 192 */ --centered /* comments 193 */ : /* comments 194 */ { + display: flex; + align-items: center; + justify-content: center; + }; +} diff --git a/tests/css_comments/selectors.scss b/tests/css_comments/selectors.scss new file mode 100644 index 00000000..94ddc437 --- /dev/null +++ b/tests/css_comments/selectors.scss @@ -0,0 +1,62 @@ +.powerPathNavigator .helm button.pressedButton, // comment 1 +.powerPathNavigator .helm button:active:not(.disabledButton), +.powerPathNavigator table.powerPathInfo th:active, +.powerPathNavigator table.powerPathInfo th:active + th:last-child { +} + +// comment 2 +.powerPathNavigator .helm button.pressedButton, +.powerPathNavigator .helm button:active:not(.disabledButton) { +} + +.foo, +// comment 3 +.bar { + display: block; +} + +.field + { + &[data-field-id="1"], // comment 4 + &[data-field-id="2"], // comment 5 + &[data-field-id="3"], // comment 6 + { + background: green; + } +} + +// comment 7 +.field +// comment 8 +{ // comment 9 + // comment 10 + &[data-field-id="1"], // comment 11 + // comment 12 + &[data-field-id="2"] // comment 13 + // comment 14 + , // comment 15 + // comment 16 + &[data-field-id="3"], // comment 17 + // comment 18 + { // comment 19 + // comment 20 + background: green; + // comment 21 + } // comment 22 + // comment 23 +} +// comment 24 + +.foo +// comment 25 +.bar +// comment 26 +{} + +.foo +// comment 27 ++ +// comment 28 +.bar +// comment 29 +{} diff --git a/tests/css_comments/source-map.css b/tests/css_comments/source-map.css new file mode 100644 index 00000000..47627e61 --- /dev/null +++ b/tests/css_comments/source-map.css @@ -0,0 +1,5 @@ +.foo { + color: red; +} + +/*# sourceMappingURL=file.css.map */ diff --git a/tests/css_comments/types.css b/tests/css_comments/types.css new file mode 100644 index 00000000..04cafe5e --- /dev/null +++ b/tests/css_comments/types.css @@ -0,0 +1,45 @@ +/* This is a basic comment */ + +/* ========================================================================== + This is a Section Comment + ========================================================================== */ + +/* + This is a sub-section comment + ========================================================================== */ + +/** + * Short description using Doxygen-style comment format + * + * The first sentence of the long description starts here and continues on this + * line for a while finally concluding here at the end of this paragraph. + * + * The long description is ideal for more detailed explanations and + * documentation. It can include example HTML, URLs, or any other information + * that is deemed necessary or useful. + * + * @tag This is a tag named 'tag' + * + * TODO: This is a todo statement that describes an atomic task to be completed + * at a later date. It wraps after 80 characters and following lines are + * indented by 2 spaces. + */ + +/** + * TODO: + * + * => Write some code + * => Make some lists + * + */ + +/* +span { + color: blue; + font-size: 1.5em; +} +*/ + +/* +Lorem ipsum dolor +*/ diff --git a/tests/css_comments/variable-declaration.scss b/tests/css_comments/variable-declaration.scss new file mode 100644 index 00000000..d9fb1878 --- /dev/null +++ b/tests/css_comments/variable-declaration.scss @@ -0,0 +1,14 @@ +$var: /* comment 1 */ all /* comment 2 */ !default /* comment 3 */ ; /* comment 4 */ + +@mixin text-color { + /* comment 5 */ + /* comment 6 */ $text-color /* comment 7 */ : /* comment 8 */ red /* comment 9 */ !default /* comment 10 */ ; /* comment 11 */ + /* comment 12 */ + color: $text-color; +} + +.error { + /* comment 13 */ + /* comment 14 */ $text-color /* comment 15 */ : /* comment 16 */ green /* comment 17 */ !global /* comment 18 */ ; /* comment 19 */ + /* comment 20 */ +} diff --git a/tests/css_empty_file/__snapshots__/jsfmt.spec.js.snap b/tests/css_empty_file/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..11ebcf01 --- /dev/null +++ b/tests/css_empty_file/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`empty-file.css 1`] = ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`; + +exports[`single-space.css 1`] = ` + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`; diff --git a/tests/css_empty_file/empty-file.css b/tests/css_empty_file/empty-file.css new file mode 100644 index 00000000..e69de29b diff --git a/tests/css_comment_in_if/jsfmt.spec.js b/tests/css_empty_file/jsfmt.spec.js similarity index 100% rename from tests/css_comment_in_if/jsfmt.spec.js rename to tests/css_empty_file/jsfmt.spec.js diff --git a/tests/css_empty_file/single-space.css b/tests/css_empty_file/single-space.css new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/tests/css_empty_file/single-space.css @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/css_import/__snapshots__/jsfmt.spec.js.snap b/tests/css_import/__snapshots__/jsfmt.spec.js.snap deleted file mode 100644 index a349f949..00000000 --- a/tests/css_import/__snapshots__/jsfmt.spec.js.snap +++ /dev/null @@ -1,46 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`directives.css 1`] = ` -@import (multiple) "foo.less"; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -@import (multiple) "foo.less"; - -`; - -exports[`multiple.css 1`] = ` -/* This isn't valid CSS, SCSS or Less, but we should be lenient and make sure -/* that nothing is lost when printing. */ -@import "one" two "three"; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -/* This isn't valid CSS, SCSS or Less, but we should be lenient and make sure -/* that nothing is lost when printing. */ -@import "one" two "three"; - -`; - -exports[`nodes.css 1`] = ` -@import "test.less" {} - -@import "test.less" { - a: b; - c: d; -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -@import "test.less" { -} - -@import "test.less" { - a: b; - c: d; -} - -`; - -exports[`url.css 1`] = ` -@import url('foo'); -$dir: 'fonts'; -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -@import url("foo"); -$dir: "fonts"; - -`; diff --git a/tests/css_import/directives.css b/tests/css_import/directives.css deleted file mode 100644 index b7452b04..00000000 --- a/tests/css_import/directives.css +++ /dev/null @@ -1 +0,0 @@ -@import (multiple) "foo.less"; diff --git a/tests/css_import/multiple.css b/tests/css_import/multiple.css deleted file mode 100644 index a0ac5280..00000000 --- a/tests/css_import/multiple.css +++ /dev/null @@ -1,3 +0,0 @@ -/* This isn't valid CSS, SCSS or Less, but we should be lenient and make sure -/* that nothing is lost when printing. */ -@import "one" two "three"; diff --git a/tests/css_import/nodes.css b/tests/css_import/nodes.css deleted file mode 100644 index f2862f02..00000000 --- a/tests/css_import/nodes.css +++ /dev/null @@ -1,6 +0,0 @@ -@import "test.less" {} - -@import "test.less" { - a: b; - c: d; -} diff --git a/tests/css_import/url.css b/tests/css_import/url.css deleted file mode 100644 index e91b68d7..00000000 --- a/tests/css_import/url.css +++ /dev/null @@ -1,2 +0,0 @@ -@import url('foo'); -$dir: 'fonts'; diff --git a/tests/css_inline_url/__snapshots__/jsfmt.spec.js.snap b/tests/css_inline_url/__snapshots__/jsfmt.spec.js.snap index 80a218f7..2f9c0918 100644 --- a/tests/css_inline_url/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_inline_url/__snapshots__/jsfmt.spec.js.snap @@ -18,6 +18,8 @@ exports[`inline_url.css 1`] = ` -fb-sprite: url(fbglyph:cross-outline, fig-white); background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII=); background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII="); + background-image: url(data:application/font-woff;charset=utf-8;base64,ThisIsNormalBut/+0ThisIsLowerCased); + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII=) center center no-repeat; } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .breadItem { @@ -26,6 +28,9 @@ exports[`inline_url.css 1`] = ` -fb-sprite: url(fbglyph:cross-outline, fig-white); background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII=); background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII="); + background-image: url(data:application/font-woff;charset=utf-8;base64,ThisIsNormalBut/+0ThisIsLowerCased); + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII=) + center center no-repeat; } `; diff --git a/tests/css_inline_url/inline_url.css b/tests/css_inline_url/inline_url.css index 410836f4..08787f38 100644 --- a/tests/css_inline_url/inline_url.css +++ b/tests/css_inline_url/inline_url.css @@ -4,4 +4,6 @@ -fb-sprite: url(fbglyph:cross-outline, fig-white); background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII=); background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII="); + background-image: url(data:application/font-woff;charset=utf-8;base64,ThisIsNormalBut/+0ThisIsLowerCased); + background: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mO4/B8AAqgB0yr7dJgAAAAASUVORK5CYII=) center center no-repeat; } diff --git a/tests/css_less/__snapshots__/jsfmt.spec.js.snap b/tests/css_less/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..9336b27b --- /dev/null +++ b/tests/css_less/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,3195 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`less.css 1`] = ` +@nice-blue: #5B83AD; +@light-blue: @nice-blue + #111; + +#header { + color: @light-blue; +} + + +#header { + color: #6c94be; +} + + +.bordered { + border-top: dotted 1px black; + border-bottom: solid 2px black; +} + + +#menu a { + color: #111; + .bordered; +} + +.post a { + color: red; + .bordered; +} + + +#header { + color: black; +} +#header .navigation { + font-size: 12px; +} +#header .logo { + width: 300px; +} + + +#header { + color: black; + .navigation { + font-size: 12px; + } + .logo { + width: 300px; + } +} + + +.clearfix { + display: block; + zoom: 1; + + &:after { + content: " "; + display: block; + font-size: 0; + height: 0; + clear: both; + visibility: hidden; + } +} + + +.screen-color { + @media screen { + color: green; + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} + + +@media screen { + .screen-color { + color: green; + } +} +@media screen and (min-width: 768px) { + .screen-color { + color: red; + } +} +@media tv { + .screen-color { + color: black; + } +} + + +#a { + color: blue; + @font-face { + src: made-up-url; + } + padding: 2 2 2 2; +} + + +#a { + color: blue; +} +@font-face { + src: made-up-url; +} +#a { + padding: 2 2 2 2; +} + + +// numbers are converted into the same units +@conversion-1: 5cm + 10mm; // result is 6cm +@conversion-2: 2 - 3cm - 5mm; // result is -1.5cm + +// conversion is impossible +@incompatible-units: 2 + 5px - 3cm; // result is 4px + +// example with variables +@base: 5%; +@filler: @base * 2; // result is 10% +@other: @base + @filler; // result is 15% + + +@base: 2cm * 3mm; // result is 6cm + + +@color: #224488 / 2; //results in #112244 +background-color: #112244 + #111; // result is #223355 + + +.weird-element { + content: ~"^//* some horrible but needed css hack"; +} + + +@base: #f04615; +@width: 0.5; + +.class { + width: percentage(@width); // returns \`50%\` + color: saturate(@base, 5%); + background-color: spin(lighten(@base, 25%), 8); +} + + +#bundle { + .button { + display: block; + border: 1px solid black; + background-color: grey; + &:hover { + background-color: white + } + } + .tab { /* ... */ } + .citation { /* ... */ } +} + + +#header a { + color: orange; + #bundle > .button; +} + + +@var: red; + +#page { + @var: white; + #header { + color: @var; // white + } +} + + +@var: red; + +#page { + #header { + color: @var; // white + } + @var: white; +} + + +/* One hell of a block +style comment! */ +@var: red; + +// Get in line! +@var: white; + + +@import "library"; // library.less +@import "typo.css"; + + +a, +.link { + color: #428bca; +} +.widget { + color: #fff; + background: #428bca; +} + + +// Variables +@link-color: #428bca; // sea blue +@link-color-hover: darken(@link-color, 10%); + +// Usage +a, +.link { + color: @link-color; +} +a:hover { + color: @link-color-hover; +} +.widget { + color: #fff; + background: @link-color; +} + + +// Variables +@my-selector: banner; + +// Usage +.@{my-selector} { + font-weight: bold; + line-height: 40px; + margin: 0 auto; +} + + +.banner { + font-weight: bold; + line-height: 40px; + margin: 0 auto; +} + + +// Variables +@images: "../img"; + +// Usage +body { + color: #444; + background: url("@{images}/white-sand.png"); +} + + +// Variables +@themes: "../../src/themes"; + +// Usage +@import "@{themes}/tidal-wave.less"; + + +@property: color; + +.widget { + @{property}: #0ee; + background-@{property}: #999; +} + + +.widget { + color: #0ee; + background-color: #999; +} + + +@fnord: "I am fnord."; +@var: "fnord"; +content: @@var; + + +content: "I am fnord."; + + +.lazy-eval { + width: @var; +} + +@var: @a; +@a: 9%; + + +.lazy-eval-scope { + width: @var; + @a: 9%; +} + +@var: @a; +@a: 100%; + + +.lazy-eval-scope { + width: 9%; +} + + +@var: 0; +.class { + @var: 1; + .brass { + @var: 2; + three: @var; + @var: 3; + } + one: @var; +} + + +.class { + one: 1; +} +.class .brass { + three: 3; +} + + +// library +@base-color: green; +@dark-color: darken(@base-color, 10%); + +// use of library +@import "library.less"; +@base-color: red; + + +nav ul { + &:extend(.inline); + background: blue; +} + + +nav ul { + &:extend(.inline); + background: blue; +} +.inline { + color: red; +} + + +nav ul { + background: blue; +} +.inline, +nav ul { + color: red; +} + + +.a:extend(.b) {} + +// the above block does the same thing as the below block +.a { + &:extend(.b); +} + + +.c:extend(.d all) { + // extends all instances of ".d" e.g. ".x.d" or ".d.x" +} +.c:extend(.d) { + // extends only instances where the selector will be output as just ".d" +} + + +.e:extend(.f) {} +.e:extend(.g) {} + +// the above an the below do the same thing +.e:extend(.f, .g) {} + + +.big-division, +.big-bag:extend(.bag), +.big-bucket:extend(.bucket) { + // body +} + + +pre:hover, +.some-class { + &:extend(div pre); +} + + +pre:hover:extend(div pre), +.some-class:extend(div pre) {} + + +.bucket { + tr { // nested ruleset with target selector + color: blue; + } +} +.some-class:extend(.bucket tr) {} // nested ruleset is recognized + + +.bucket tr, +.some-class { + color: blue; +} + + +.bucket { + tr & { // nested ruleset with target selector + color: blue; + } +} +.some-class:extend(tr .bucket) {} // nested ruleset is recognized + + +tr .bucket, +.some-class { + color: blue; +} + + +.a.class, +.class.a, +.class > .a { + color: blue; +} +.test:extend(.class) {} // this will NOT match the any selectors above + + +*.class { + color: blue; +} +.noStar:extend(.class) {} // this will NOT match the *.class selector + + +*.class { + color: blue; +} + + +link:hover:visited { + color: blue; +} +.selector:extend(link:visited:hover) {} + + +link:hover:visited { + color: blue; +} + + +:nth-child(1n+3) { + color: blue; +} +.child:extend(:nth-child(n+3)) {} + + +:nth-child(1n+3) { + color: blue; +} + + +[title=identifier] { + color: blue; +} +[title='identifier'] { + color: blue; +} +[title="identifier"] { + color: blue; +} + +.noQuote:extend([title=identifier]) {} +.singleQuote:extend([title='identifier']) {} +.doubleQuote:extend([title="identifier"]) {} + + +[title=identifier], +.noQuote, +.singleQuote, +.doubleQuote { + color: blue; +} + +[title='identifier'], +.noQuote, +.singleQuote, +.doubleQuote { + color: blue; +} + +[title="identifier"], +.noQuote, +.singleQuote, +.doubleQuote { + color: blue; +} + + +.a.b.test, +.test.c { + color: orange; +} +.test { + &:hover { + color: green; + } +} + +.replacement:extend(.test all) {} + + +.a.b.test, +.test.c, +.a.b.replacement, +.replacement.c { + color: orange; +} +.test:hover, +.replacement:hover { + color: green; +} + + +@variable: .bucket; +@{variable} { // interpolated selector + color: blue; +} +.some-class:extend(.bucket) {} // does nothing, no match is found + + +.bucket { + color: blue; +} +.some-class:extend(@{variable}) {} // interpolated selector matches nothing +@variable: .bucket; + + +.bucket { + color: blue; +} + + +.bucket { + color: blue; +} +@{variable}:extend(.bucket) {} +@variable: .selector; + + +.bucket, .selector { + color: blue; +} + + +@media print { + .screenClass:extend(.selector) {} // extend inside media + .selector { // this will be matched - it is in the same media + color: black; + } +} +.selector { // ruleset on top of style sheet - extend ignores it + color: red; +} +@media screen { + .selector { // ruleset inside another media - extend ignores it + color: blue; + } +} + + +@media print { + .selector, + .screenClass { /* ruleset inside the same media was extended */ + color: black; + } +} +.selector { /* ruleset on top of style sheet was ignored */ + color: red; +} +@media screen { + .selector { /* ruleset inside another media was ignored */ + color: blue; + } +} + + +@media screen { + .screenClass:extend(.selector) {} // extend inside media + @media (min-width: 1023px) { + .selector { // ruleset inside nested media - extend ignores it + color: blue; + } + } +} + + +@media screen and (min-width: 1023px) { + .selector { /* ruleset inside another nested media was ignored */ + color: blue; + } +} + + +@media screen { + .selector { /* ruleset inside nested media - top level extend works */ + color: blue; + } + @media (min-width: 1023px) { + .selector { /* ruleset inside nested media - top level extend works */ + color: blue; + } + } +} + +.topLevel:extend(.selector) {} /* top level extend matches everything */ + + +@media screen { + .selector, + .topLevel { /* ruleset inside media was extended */ + color: blue; + } +} +@media screen and (min-width: 1023px) { + .selector, + .topLevel { /* ruleset inside nested media was extended */ + color: blue; + } +} + + +.alert-info, +.widget { + /* declarations */ +} + +.alert:extend(.alert-info, .widget) {} + + +.alert-info, +.widget, +.alert, +.alert { + /* declarations */ +} + + +.animal { + background-color: black; + color: white; +} + + +.animal { + background-color: black; + color: white; +} +.bear { + background-color: brown; +} + +.animal { + background-color: black; + color: white; +} +.bear { + &:extend(.animal); + background-color: brown; +} + + +.my-inline-block() { + display: inline-block; + font-size: 0; +} +.thing1 { + .my-inline-block; +} +.thing2 { + .my-inline-block; +} + + +.thing1 { + display: inline-block; + font-size: 0; +} +.thing2 { + display: inline-block; + font-size: 0; +} + + +.my-inline-block { + display: inline-block; + font-size: 0; +} +.thing1 { + &:extend(.my-inline-block); +} +.thing2 { + &:extend(.my-inline-block); +} + + +.my-inline-block, +.thing1, +.thing2 { + display: inline-block; + font-size: 0; +} + + +li.list > a { + // list styles +} +button.list-style { + &:extend(li.list > a); // use the same list styles +} + + +.a, #b { + color: red; +} +.mixin-class { + .a(); +} +.mixin-id { + #b(); +} + + +.a, #b { + color: red; +} +.mixin-class { + color: red; +} +.mixin-id { + color: red; +} + + +// these two statements do the same thing: +.a(); +.a; + + +.my-mixin { + color: black; +} +.my-other-mixin() { + background: white; +} +.class { + .my-mixin; + .my-other-mixin; +} + + +.my-mixin { + color: black; +} +.class { + color: black; + background: white; +} + + +.my-hover-mixin() { + &:hover { + border: 1px solid red; + } +} +button { + .my-hover-mixin(); +} + + +button:hover { + border: 1px solid red; +} + + +#outer { + .inner { + color: red; + } +} + +.c { + #outer > .inner; +} + + +// all do the same thing +#outer > .inner; +#outer > .inner(); +#outer .inner; +#outer .inner(); +#outer.inner; +#outer.inner(); + + +#my-library { + .my-mixin() { + color: black; + } +} +// which can be used like this +.class { + #my-library > .my-mixin(); +} + + +#namespace when (@mode=huge) { + .mixin() { /* */ } +} + +#namespace { + .mixin() when (@mode=huge) { /* */ } +} + + +#sp_1 when (default()) { + #sp_2 when (default()) { + .mixin() when not(default()) { /* */ } + } +} + + +.foo (@bg: #f5f5f5, @color: #900) { + background: @bg; + color: @color; +} +.unimportant { + .foo(); +} +.important { + .foo() !important; +} + + +.unimportant { + background: #f5f5f5; + color: #900; +} +.important { + background: #f5f5f5 !important; + color: #900 !important; +} + + +.border-radius(@radius) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + + +#header { + .border-radius(4px); +} +.button { + .border-radius(6px); +} + + +.border-radius(@radius: 5px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + + +#header { + .border-radius; +} + + +.wrap() { + text-wrap: wrap; + white-space: -moz-pre-wrap; + white-space: pre-wrap; + word-wrap: break-word; +} + +pre { .wrap } + + +pre { + text-wrap: wrap; + white-space: -moz-pre-wrap; + white-space: pre-wrap; + word-wrap: break-word; +} + + +.mixin(@color) { + color-1: @color; +} +.mixin(@color; @padding: 2) { + color-2: @color; + padding-2: @padding; +} +.mixin(@color; @padding; @margin: 2) { + color-3: @color; + padding-3: @padding; + margin: @margin @margin @margin @margin; +} +.some .selector div { + .mixin(#008000); +} + + +.some .selector div { + color-1: #008000; + color-2: #008000; + padding-2: 2; +} + + +.mixin(@color: black; @margin: 10px; @padding: 20px) { + color: @color; + margin: @margin; + padding: @padding; +} +.class1 { + .mixin(@margin: 20px; @color: #33acfe); +} +.class2 { + .mixin(#efca44; @padding: 40px); +} + + +.class1 { + color: #33acfe; + margin: 20px; + padding: 20px; +} +.class2 { + color: #efca44; + margin: 10px; + padding: 40px; +} + + +.box-shadow(@x: 0; @y: 0; @blur: 1px; @color: #000) { + -webkit-box-shadow: @arguments; + -moz-box-shadow: @arguments; + box-shadow: @arguments; +} +.big-block { + .box-shadow(2px; 5px); +} + + +.big-block { + -webkit-box-shadow: 2px 5px 1px #000; + -moz-box-shadow: 2px 5px 1px #000; + box-shadow: 2px 5px 1px #000; +} + + +.mixin(/* ... */) {} // matches 0-N arguments +.mixin() {} // matches exactly 0 arguments +.mixin(@a: 1) {} // matches 0-1 arguments +.mixin(@a: 1; /* ... */) {} // matches 0-N arguments +.mixin(@a; /* ... */) {} // matches 1-N arguments + + +.mixin(@a; @rest...) { + // @rest is bound to arguments after @a + // @arguments is bound to all arguments +} + + +.mixin(@s; @color) { /* ... */ } + +.class { + .mixin(@switch; #888); +} + + +.mixin(dark; @color) { + color: darken(@color, 10%); +} +.mixin(light; @color) { + color: lighten(@color, 10%); +} +.mixin(@_; @color) { + display: block; +} + + +@switch: light; + +.class { + .mixin(@switch; #888); +} + + +.class { + color: #a2a2a2; + display: block; +} + + +.mixin(@a) { + color: @a; +} +.mixin(@a; @b) { + color: fade(@a; @b); +} + + +.mixin() { + @width: 100%; + @height: 200px; +} + +.caller { + .mixin(); + width: @width; + height: @height; +} + + +.caller { + width: 100%; + height: 200px; +} + + +.average(@x, @y) { + @average: ((@x + @y) / 2); +} + +div { + .average(16px, 50px); // "call" the mixin + padding: @average; // use its "return" value +} + + +div { + padding: 33px; +} + + +.mixin() { + @size: in-mixin; + @definedOnlyInMixin: in-mixin; +} + +.class { + margin: @size @definedOnlyInMixin; + .mixin(); +} + +@size: globally-defined-value; // callers parent scope - no protection + + +.class { + margin: in-mixin in-mixin; +} + + +.unlock(@value) { // outer mixin + .doSomething() { // nested mixin + declaration: @value; + } +} + +#namespace { + .unlock(5); // unlock doSomething mixin + .doSomething(); //nested mixin was copied here and is usable +} + + +#namespace { + declaration: 5; +} + + +// declare detached ruleset +@detached-ruleset: { background: red; }; + +// use detached ruleset +.top { + @detached-ruleset(); +} + + +.top { + background: red; +} + + +.desktop-and-old-ie(@rules) { + @media screen and (min-width: 1200px) { @rules(); } + html.lt-ie9 & { @rules(); } +} + +header { + background-color: blue; + + .desktop-and-old-ie({ + background-color: red; + }); +} + + +header { + background-color: blue; +} +@media screen and (min-width: 1200px) { + header { + background-color: red; + } +} +html.lt-ie9 header { + background-color: red; +} + + +@my-ruleset: { + .my-selector { + background-color: black; + } + }; + + +@my-ruleset: { + .my-selector { + @media tv { + background-color: black; + } + } + }; +@media (orientation:portrait) { + @my-ruleset(); +} + + +@media (orientation: portrait) and tv { + .my-selector { + background-color: black; + } +} + + +// detached ruleset with a mixin +@detached-ruleset: { + .mixin() { + color:blue; + } +}; +// call detached ruleset +.caller { + @detached-ruleset(); + .mixin(); +} + + +.caller { + color: blue; +} + + +@detached-ruleset: { + @color:blue; // this variable is private +}; +.caller { + color: @color; // syntax error +} + + +@detached-ruleset: { + caller-variable: @caller-variable; // variable is undefined here + .caller-mixin(); // mixin is undefined here +}; + +selector { + // use detached ruleset + @detached-ruleset(); + + // define variable and mixin needed inside the detached ruleset + @caller-variable: value; + .caller-mixin() { + variable: declaration; + } +} + + +selector { + caller-variable: value; + variable: declaration; +} + + +@variable: global; +@detached-ruleset: { + // will use global variable, because it is accessible + // from detached-ruleset definition + variable: @variable; +}; + +selector { + @detached-ruleset(); + @variable: value; // variable defined in caller - will be ignored +} + + +selector { + variable: global; +} + + +@detached-1: { scope-detached: @one @two; }; +.one { + @one: visible; + .two { + @detached-2: @detached-1; // copying/renaming ruleset + @two: visible; // ruleset can not see this variable + } +} + +.use-place { + .one > .two(); + @detached-2(); +} + + +#space { + .importer-1() { + @detached: { scope-detached: @variable; }; // define detached ruleset + } +} + +.importer-2() { + @variable: value; // unlocked detached ruleset CAN see this variable + #space > .importer-1(); // unlock/import detached ruleset +} + +.use-place { + .importer-2(); // unlock/import detached ruleset second time + @detached(); +} + + +.use-place { + scope-detached: value; +} + +@my-ruleset:{.my-selector{@media tv{background-color:black;}}}; + +.foo { + background: #900; +} +@import "this-is-valid.less"; + + +@import "foo"; // foo.less is imported +@import "foo.less"; // foo.less is imported +@import "foo.php"; // foo.php imported as a less file +@import "foo.css"; // statement left in place, as-is + + +.navbar:extend(.navbar all) {} + + +@import (less) "foo.css"; + + +@import (css) "foo.less"; + + +@import "foo.less"; + + +@import (once) "foo.less"; +@import (once) "foo.less"; // this statement will be ignored + + +// file: foo.less +.a { + color: green; +} +// file: main.less +@import (multiple) "foo.less"; +@import (multiple) "foo.less"; + + +.a { + color: green; +} +.a { + color: green; +} + + +.mixin (@a) when (lightness(@a) >= 50%) { + background-color: black; +} +.mixin (@a) when (lightness(@a) < 50%) { + background-color: white; +} +.mixin (@a) { + color: @a; +} + + +.class1 { .mixin(#ddd) } +.class2 { .mixin(#555) } + + +.class1 { + background-color: black; + color: #ddd; +} +.class2 { + background-color: white; + color: #555; +} + + +.truth (@a) when (@a) { /* ... */ } +.truth (@a) when (@a = true) { /* ... */ } + + +.class { + .truth(40); // Will not match any of the above definitions. +} + + +@media: mobile; + +.mixin (@a) when (@media = mobile) { /* ... */ } +.mixin (@a) when (@media = desktop) { /* ... */ } + +.max (@a; @b) when (@a > @b) { width: @a } +.max (@a; @b) when (@a < @b) { width: @b } + + +.mixin (@a) when (isnumber(@a)) and (@a > 0) { /* ... */ } + + +.mixin (@a) when (@a > 10), (@a < -10) { /* ... */ } + + +.mixin (@b) when not (@b > 0) { /* ... */ } + + +.mixin (@a; @b: 0) when (isnumber(@b)) { /* ... */ } +.mixin (@a; @b: black) when (iscolor(@b)) { /* ... */ } + + +.mixin (@a) when (@a > 0) { /* ... */ } +.mixin (@a) when (default()) { /* ... */ } // matches only if first mixin does not, i.e. when @a <= 0 + + +.my-optional-style() when (@my-option = true) { + button { + color: white; + } +} +.my-optional-style(); + + +button when (@my-option = true) { + color: white; +} + + +& when (@my-option = true) { + button { + color: white; + } + a { + color: blue; + } +} + + +.loop(@counter) when (@counter > 0) { + .loop((@counter - 1)); // next iteration + width: (10px * @counter); // code for each iteration +} + +div { + .loop(5); // launch the loop +} + + +div { + width: 10px; + width: 20px; + width: 30px; + width: 40px; + width: 50px; +} + + +.generate-columns(4); + +.generate-columns(@n, @i: 1) when (@i =< @n) { + .column-@{i} { + width: (@i * 100% / @n); + } + .generate-columns(@n, (@i + 1)); +} + + +.column-1 { + width: 25%; +} +.column-2 { + width: 50%; +} +.column-3 { + width: 75%; +} +.column-4 { + width: 100%; +} + + +.mixin() { + box-shadow+: inset 0 0 10px #555; +} +.myclass { + .mixin(); + box-shadow+: 0 0 20px black; +} + + +.myclass { + box-shadow: inset 0 0 10px #555, 0 0 20px black; +} + + +.mixin() { + transform+_: scale(2); +} +.myclass { + .mixin(); + transform+_: rotate(15deg); +} + + +.myclass { + transform: scale(2) rotate(15deg); +} + + +a { + color: blue; + &:hover { + color: green; + } +} + + +a { + color: blue; +} + +a:hover { + color: green; +} + + +.button { + &-ok { + background-image: url("ok.png"); + } + &-cancel { + background-image: url("cancel.png"); + } + + &-custom { + background-image: url("custom.png"); + } +} + + +.button-ok { + background-image: url("ok.png"); +} +.button-cancel { + background-image: url("cancel.png"); +} +.button-custom { + background-image: url("custom.png"); +} + + +.link { + & + & { + color: red; + } + + & & { + color: green; + } + + && { + color: blue; + } + + &, &ish { + color: cyan; + } +} + + +.link + .link { + color: red; +} +.link .link { + color: green; +} +.link.link { + color: blue; +} +.link, .linkish { + color: cyan; +} + + +.grand { + .parent { + & > & { + color: red; + } + + & & { + color: green; + } + + && { + color: blue; + } + + &, &ish { + color: cyan; + } + } +} + + +.grand .parent > .grand .parent { + color: red; +} +.grand .parent .grand .parent { + color: green; +} +.grand .parent.grand .parent { + color: blue; +} +.grand .parent, +.grand .parentish { + color: cyan; +} + + +.header { + .menu { + border-radius: 5px; + .no-borderradius & { + background-image: url('images/button-background.png'); + } + } +} + + +.header .menu { + border-radius: 5px; +} +.no-borderradius .header .menu { + background-image: url('images/button-background.png'); +} + + +p, a, ul, li { + border-top: 2px dotted #366; + & + & { + border-top: 0; + } +} + + +p, +a, +ul, +li { + border-top: 2px dotted #366; +} +p + p, +p + a, +p + ul, +p + li, +a + p, +a + a, +a + ul, +a + li, +ul + p, +ul + a, +ul + ul, +ul + li, +li + p, +li + a, +li + ul, +li + li { + border-top: 0; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@nice-blue: #5b83ad; +@light-blue: @nice-blue + #111; + +#header { + color: @light-blue; +} + +#header { + color: #6c94be; +} + +.bordered { + border-top: dotted 1px black; + border-bottom: solid 2px black; +} + +#menu a { + color: #111; + .bordered; +} + +.post a { + color: red; + .bordered; +} + +#header { + color: black; +} +#header .navigation { + font-size: 12px; +} +#header .logo { + width: 300px; +} + +#header { + color: black; + .navigation { + font-size: 12px; + } + .logo { + width: 300px; + } +} + +.clearfix { + display: block; + zoom: 1; + + &:after { + content: " "; + display: block; + font-size: 0; + height: 0; + clear: both; + visibility: hidden; + } +} + +.screen-color { + @media screen { + color: green; + @media (min-width: 768px) { + color: red; + } + } + @media tv { + color: black; + } +} + +@media screen { + .screen-color { + color: green; + } +} +@media screen and (min-width: 768px) { + .screen-color { + color: red; + } +} +@media tv { + .screen-color { + color: black; + } +} + +#a { + color: blue; + @font-face { + src: made-up-url; + } + padding: 2 2 2 2; +} + +#a { + color: blue; +} +@font-face { + src: made-up-url; +} +#a { + padding: 2 2 2 2; +} + +// numbers are converted into the same units +@conversion-1: 5cm + 10mm; // result is 6cm +@conversion-2: 2 - 3cm - 5mm; // result is -1.5cm + +// conversion is impossible +@incompatible-units: 2 + 5px - 3cm; // result is 4px + +// example with variables +@base: 5%; +@filler: @base * 2; // result is 10% +@other: @base + @filler; // result is 15% + +@base: 2cm * 3mm; // result is 6cm + +@color: #224488 / 2; //results in #112244 +background-color: #112244 + #111; // result is #223355 + +.weird-element { + content: ~"^//* some horrible but needed css hack"; +} + +@base: #f04615; +@width: 0.5; + +.class { + width: percentage(@width); // returns \`50%\` + color: saturate(@base, 5%); + background-color: spin(lighten(@base, 25%), 8); +} + +#bundle { + .button { + display: block; + border: 1px solid black; + background-color: grey; + &:hover { + background-color: white; + } + } + .tab { + /* ... */ + } + .citation { + /* ... */ + } +} + +#header a { + color: orange; + #bundle > .button; +} + +@var: red; + +#page { + @var: white; + #header { + color: @var; // white + } +} + +@var: red; + +#page { + #header { + color: @var; // white + } + @var: white; +} + +/* One hell of a block +style comment! */ +@var: red; + +// Get in line! +@var: white; + +@import "library"; // library.less +@import "typo.css"; + +a, +.link { + color: #428bca; +} +.widget { + color: #fff; + background: #428bca; +} + +// Variables +@link-color: #428bca; // sea blue +@link-color-hover: darken(@link-color, 10%); + +// Usage +a, +.link { + color: @link-color; +} +a:hover { + color: @link-color-hover; +} +.widget { + color: #fff; + background: @link-color; +} + +// Variables +@my-selector: banner; + +// Usage +.@{my-selector} { + font-weight: bold; + line-height: 40px; + margin: 0 auto; +} + +.banner { + font-weight: bold; + line-height: 40px; + margin: 0 auto; +} + +// Variables +@images: "../img"; + +// Usage +body { + color: #444; + background: url("@{images}/white-sand.png"); +} + +// Variables +@themes: "../../src/themes"; + +// Usage +@import "@{themes}/tidal-wave.less"; + +@property: color; + +.widget { + @{property}: #0ee; + background-@{property}: #999; +} + +.widget { + color: #0ee; + background-color: #999; +} + +@fnord: "I am fnord."; +@var: "fnord"; +content: @@var; + +content: "I am fnord."; + +.lazy-eval { + width: @var; +} + +@var: @a; +@a: 9%; + +.lazy-eval-scope { + width: @var; + @a: 9%; +} + +@var: @a; +@a: 100%; + +.lazy-eval-scope { + width: 9%; +} + +@var: 0; +.class { + @var: 1; + .brass { + @var: 2; + three: @var; + @var: 3; + } + one: @var; +} + +.class { + one: 1; +} +.class .brass { + three: 3; +} + +// library +@base-color: green; +@dark-color: darken(@base-color, 10%); + +// use of library +@import "library.less"; +@base-color: red; + +nav ul { + &:extend(.inline); + background: blue; +} + +nav ul { + &:extend(.inline); + background: blue; +} +.inline { + color: red; +} + +nav ul { + background: blue; +} +.inline, +nav ul { + color: red; +} + +.a:extend(.b) { +} + +// the above block does the same thing as the below block +.a { + &:extend(.b); +} + +.c:extend(.d all) { + // extends all instances of ".d" e.g. ".x.d" or ".d.x" +} +.c:extend(.d) { + // extends only instances where the selector will be output as just ".d" +} + +.e:extend(.f) { +} +.e:extend(.g) { +} + +// the above an the below do the same thing +.e:extend(.f, .g) { +} + +.big-division, +.big-bag:extend(.bag), +.big-bucket:extend(.bucket) { + // body +} + +pre:hover, +.some-class { + &:extend(div pre); +} + +pre:hover:extend(div pre), +.some-class:extend(div pre) { +} + +.bucket { + tr { + // nested ruleset with target selector + color: blue; + } +} +.some-class:extend(.bucket tr) { +} // nested ruleset is recognized + +.bucket tr, +.some-class { + color: blue; +} + +.bucket { + tr & { + // nested ruleset with target selector + color: blue; + } +} +.some-class:extend(tr .bucket) { +} // nested ruleset is recognized + +tr .bucket, +.some-class { + color: blue; +} + +.a.class, +.class.a, +.class > .a { + color: blue; +} +.test:extend(.class) { +} // this will NOT match the any selectors above + +*.class { + color: blue; +} +.noStar:extend(.class) { +} // this will NOT match the *.class selector + +*.class { + color: blue; +} + +link:hover:visited { + color: blue; +} +.selector:extend(link:visited:hover) { +} + +link:hover:visited { + color: blue; +} + +:nth-child(1n + 3) { + color: blue; +} +.child:extend(:nth-child(n + 3)) { +} + +:nth-child(1n + 3) { + color: blue; +} + +[title="identifier"] { + color: blue; +} +[title="identifier"] { + color: blue; +} +[title="identifier"] { + color: blue; +} + +.noQuote:extend([title="identifier"]) { +} +.singleQuote:extend([title="identifier"]) { +} +.doubleQuote:extend([title="identifier"]) { +} + +[title="identifier"], +.noQuote, +.singleQuote, +.doubleQuote { + color: blue; +} + +[title="identifier"], +.noQuote, +.singleQuote, +.doubleQuote { + color: blue; +} + +[title="identifier"], +.noQuote, +.singleQuote, +.doubleQuote { + color: blue; +} + +.a.b.test, +.test.c { + color: orange; +} +.test { + &:hover { + color: green; + } +} + +.replacement:extend(.test all) { +} + +.a.b.test, +.test.c, +.a.b.replacement, +.replacement.c { + color: orange; +} +.test:hover, +.replacement:hover { + color: green; +} + +@variable: .bucket; +@{variable} { + // interpolated selector + color: blue; +} +.some-class:extend(.bucket) { +} // does nothing, no match is found + +.bucket { + color: blue; +} +.some-class:extend(@{variable}) { +} // interpolated selector matches nothing +@variable: .bucket; + +.bucket { + color: blue; +} + +.bucket { + color: blue; +} +@{variable}:extend(.bucket) { +} +@variable: .selector; + +.bucket, +.selector { + color: blue; +} + +@media print { + .screenClass:extend(.selector) { + } // extend inside media + .selector { + // this will be matched - it is in the same media + color: black; + } +} +.selector { + // ruleset on top of style sheet - extend ignores it + color: red; +} +@media screen { + .selector { + // ruleset inside another media - extend ignores it + color: blue; + } +} + +@media print { + .selector, + .screenClass { + /* ruleset inside the same media was extended */ + color: black; + } +} +.selector { + /* ruleset on top of style sheet was ignored */ + color: red; +} +@media screen { + .selector { + /* ruleset inside another media was ignored */ + color: blue; + } +} + +@media screen { + .screenClass:extend(.selector) { + } // extend inside media + @media (min-width: 1023px) { + .selector { + // ruleset inside nested media - extend ignores it + color: blue; + } + } +} + +@media screen and (min-width: 1023px) { + .selector { + /* ruleset inside another nested media was ignored */ + color: blue; + } +} + +@media screen { + .selector { + /* ruleset inside nested media - top level extend works */ + color: blue; + } + @media (min-width: 1023px) { + .selector { + /* ruleset inside nested media - top level extend works */ + color: blue; + } + } +} + +.topLevel:extend(.selector) { +} /* top level extend matches everything */ + +@media screen { + .selector, + .topLevel { + /* ruleset inside media was extended */ + color: blue; + } +} +@media screen and (min-width: 1023px) { + .selector, + .topLevel { + /* ruleset inside nested media was extended */ + color: blue; + } +} + +.alert-info, +.widget { + /* declarations */ +} + +.alert:extend(.alert-info, .widget) { +} + +.alert-info, +.widget, +.alert, +.alert { + /* declarations */ +} + +.animal { + background-color: black; + color: white; +} + +.animal { + background-color: black; + color: white; +} +.bear { + background-color: brown; +} + +.animal { + background-color: black; + color: white; +} +.bear { + &:extend(.animal); + background-color: brown; +} + +.my-inline-block() { + display: inline-block; + font-size: 0; +} +.thing1 { + .my-inline-block; +} +.thing2 { + .my-inline-block; +} + +.thing1 { + display: inline-block; + font-size: 0; +} +.thing2 { + display: inline-block; + font-size: 0; +} + +.my-inline-block { + display: inline-block; + font-size: 0; +} +.thing1 { + &:extend(.my-inline-block); +} +.thing2 { + &:extend(.my-inline-block); +} + +.my-inline-block, +.thing1, +.thing2 { + display: inline-block; + font-size: 0; +} + +li.list > a { + // list styles +} +button.list-style { + &:extend(li.list > a); // use the same list styles +} + +.a, +#b { + color: red; +} +.mixin-class { + .a(); +} +.mixin-id { + #b(); +} + +.a, +#b { + color: red; +} +.mixin-class { + color: red; +} +.mixin-id { + color: red; +} + +// these two statements do the same thing: +.a(); +.a; + +.my-mixin { + color: black; +} +.my-other-mixin() { + background: white; +} +.class { + .my-mixin; + .my-other-mixin; +} + +.my-mixin { + color: black; +} +.class { + color: black; + background: white; +} + +.my-hover-mixin() { + &:hover { + border: 1px solid red; + } +} +button { + .my-hover-mixin(); +} + +button:hover { + border: 1px solid red; +} + +#outer { + .inner { + color: red; + } +} + +.c { + #outer > .inner; +} + +// all do the same thing +#outer > .inner; +#outer > .inner(); +#outer .inner; +#outer .inner(); +#outer.inner; +#outer.inner(); + +#my-library { + .my-mixin() { + color: black; + } +} +// which can be used like this +.class { + #my-library > .my-mixin(); +} + +#namespace when (@mode=huge) { + .mixin() { + /* */ + } +} + +#namespace { + .mixin() when (@mode=huge) { + /* */ + } +} + +#sp_1 when (default()) { + #sp_2 when (default()) { + .mixin() when not(default()) { + /* */ + } + } +} + +.foo (@bg: #f5f5f5, @color: #900) { + background: @bg; + color: @color; +} +.unimportant { + .foo(); +} +.important { + .foo() !important; +} + +.unimportant { + background: #f5f5f5; + color: #900; +} +.important { + background: #f5f5f5 !important; + color: #900 !important; +} + +.border-radius(@radius) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + +#header { + .border-radius(4px); +} +.button { + .border-radius(6px); +} + +.border-radius(@radius: 5px) { + -webkit-border-radius: @radius; + -moz-border-radius: @radius; + border-radius: @radius; +} + +#header { + .border-radius; +} + +.wrap() { + text-wrap: wrap; + white-space: -moz-pre-wrap; + white-space: pre-wrap; + word-wrap: break-word; +} + +pre { + .wrap; +} + +pre { + text-wrap: wrap; + white-space: -moz-pre-wrap; + white-space: pre-wrap; + word-wrap: break-word; +} + +.mixin(@color) { + color-1: @color; +} +.mixin(@color; @padding: 2) { + color-2: @color; + padding-2: @padding; +} +.mixin(@color; @padding; @margin: 2) { + color-3: @color; + padding-3: @padding; + margin: @margin @margin @margin @margin; +} +.some .selector div { + .mixin(#008000); +} + +.some .selector div { + color-1: #008000; + color-2: #008000; + padding-2: 2; +} + +.mixin(@color: black; @margin: 10px; @padding: 20px) { + color: @color; + margin: @margin; + padding: @padding; +} +.class1 { + .mixin(@margin: 20px; @color: #33acfe); +} +.class2 { + .mixin(#efca44; @padding: 40px); +} + +.class1 { + color: #33acfe; + margin: 20px; + padding: 20px; +} +.class2 { + color: #efca44; + margin: 10px; + padding: 40px; +} + +.box-shadow(@x: 0; @y: 0; @blur: 1px; @color: #000) { + -webkit-box-shadow: @arguments; + -moz-box-shadow: @arguments; + box-shadow: @arguments; +} +.big-block { + .box-shadow(2px; 5px); +} + +.big-block { + -webkit-box-shadow: 2px 5px 1px #000; + -moz-box-shadow: 2px 5px 1px #000; + box-shadow: 2px 5px 1px #000; +} + +.mixin(/* ... */) { +} // matches 0-N arguments +.mixin() { +} // matches exactly 0 arguments +.mixin(@a: 1) { +} // matches 0-1 arguments +.mixin(@a: 1; /* ... */) { +} // matches 0-N arguments +.mixin(@a; /* ... */) { +} // matches 1-N arguments + +.mixin(@a; @rest...) { + // @rest is bound to arguments after @a + // @arguments is bound to all arguments +} + +.mixin(@s; @color) { + /* ... */ +} + +.class { + .mixin(@switch; #888); +} + +.mixin(dark; @color) { + color: darken(@color, 10%); +} +.mixin(light; @color) { + color: lighten(@color, 10%); +} +.mixin(@_; @color) { + display: block; +} + +@switch: light; + +.class { + .mixin(@switch; #888); +} + +.class { + color: #a2a2a2; + display: block; +} + +.mixin(@a) { + color: @a; +} +.mixin(@a; @b) { + color: fade(@a; @b); +} + +.mixin() { + @width: 100%; + @height: 200px; +} + +.caller { + .mixin(); + width: @width; + height: @height; +} + +.caller { + width: 100%; + height: 200px; +} + +.average(@x, @y) { + @average: ((@x + @y) / 2); +} + +div { + .average(16px, 50px); // "call" the mixin + padding: @average; // use its "return" value +} + +div { + padding: 33px; +} + +.mixin() { + @size: in-mixin; + @definedOnlyInMixin: in-mixin; +} + +.class { + margin: @size @definedOnlyInMixin; + .mixin(); +} + +@size: globally-defined-value; // callers parent scope - no protection + +.class { + margin: in-mixin in-mixin; +} + +.unlock(@value) { + // outer mixin + .doSomething() { + // nested mixin + declaration: @value; + } +} + +#namespace { + .unlock(5); // unlock doSomething mixin + .doSomething(); //nested mixin was copied here and is usable +} + +#namespace { + declaration: 5; +} + +// declare detached ruleset +@detached-ruleset: { + background: red; +}; + +// use detached ruleset +.top { + @detached-ruleset(); +} + +.top { + background: red; +} + +.desktop-and-old-ie(@rules) { + @media screen and (min-width: 1200px) { + @rules(); + } + html.lt-ie9 & { + @rules(); + } +} + +header { + background-color: blue; + + .desktop-and-old-ie({ + background-color: red; + }); +} + +header { + background-color: blue; +} +@media screen and (min-width: 1200px) { + header { + background-color: red; + } +} +html.lt-ie9 header { + background-color: red; +} + +@my-ruleset: { + .my-selector { + background-color: black; + } +}; + +@my-ruleset: { + .my-selector { + @media tv { + background-color: black; + } + } +}; +@media (orientation: portrait) { + @my-ruleset(); +} + +@media (orientation: portrait) and tv { + .my-selector { + background-color: black; + } +} + +// detached ruleset with a mixin +@detached-ruleset: { + .mixin() { + color: blue; + } +}; +// call detached ruleset +.caller { + @detached-ruleset(); + .mixin(); +} + +.caller { + color: blue; +} + +@detached-ruleset: { + @color: blue; // this variable is private +}; +.caller { + color: @color; // syntax error +} + +@detached-ruleset: { + caller-variable: @caller-variable; // variable is undefined here + .caller-mixin(); // mixin is undefined here +}; + +selector { + // use detached ruleset + @detached-ruleset(); + + // define variable and mixin needed inside the detached ruleset + @caller-variable: value; + .caller-mixin() { + variable: declaration; + } +} + +selector { + caller-variable: value; + variable: declaration; +} + +@variable: global; +@detached-ruleset: { + // will use global variable, because it is accessible + // from detached-ruleset definition + variable: @variable; +}; + +selector { + @detached-ruleset(); + @variable: value; // variable defined in caller - will be ignored +} + +selector { + variable: global; +} + +@detached-1: { + scope-detached: @one @two; +}; +.one { + @one: visible; + .two { + @detached-2: @detached-1; // copying/renaming ruleset + @two: visible; // ruleset can not see this variable + } +} + +.use-place { + .one > .two(); + @detached-2(); +} + +#space { + .importer-1() { + @detached: { + scope-detached: @variable; + }; // define detached ruleset + } +} + +.importer-2() { + @variable: value; // unlocked detached ruleset CAN see this variable + #space > .importer-1(); // unlock/import detached ruleset +} + +.use-place { + .importer-2(); // unlock/import detached ruleset second time + @detached(); +} + +.use-place { + scope-detached: value; +} + +@my-ruleset: { + .my-selector { + @media tv { + background-color: black; + } + } +}; + +.foo { + background: #900; +} +@import "this-is-valid.less"; + +@import "foo"; // foo.less is imported +@import "foo.less"; // foo.less is imported +@import "foo.php"; // foo.php imported as a less file +@import "foo.css"; // statement left in place, as-is + +.navbar:extend(.navbar all) { +} + +@import (less) "foo.css"; + +@import (css) "foo.less"; + +@import "foo.less"; + +@import (once) "foo.less"; +@import (once) "foo.less"; // this statement will be ignored + +// file: foo.less +.a { + color: green; +} +// file: main.less +@import (multiple) "foo.less"; +@import (multiple) "foo.less"; + +.a { + color: green; +} +.a { + color: green; +} + +.mixin (@a) when (lightness(@a) >= 50%) { + background-color: black; +} +.mixin (@a) when (lightness(@a) < 50%) { + background-color: white; +} +.mixin (@a) { + color: @a; +} + +.class1 { + .mixin(#ddd); +} +.class2 { + .mixin(#555); +} + +.class1 { + background-color: black; + color: #ddd; +} +.class2 { + background-color: white; + color: #555; +} + +.truth (@a) when (@a) { + /* ... */ +} +.truth (@a) when (@a = true) { + /* ... */ +} + +.class { + .truth(40); // Will not match any of the above definitions. +} + +@media: mobile; + +.mixin (@a) when (@media = mobile) { + /* ... */ +} +.mixin (@a) when (@media = desktop) { + /* ... */ +} + +.max (@a; @b) when (@a > @b) { + width: @a; +} +.max (@a; @b) when (@a < @b) { + width: @b; +} + +.mixin (@a) when (isnumber(@a)) and (@a > 0) { + /* ... */ +} + +.mixin (@a) when (@a > 10), (@a < -10) { + /* ... */ +} + +.mixin (@b) when not (@b > 0) { + /* ... */ +} + +.mixin (@a; @b: 0) when (isnumber(@b)) { + /* ... */ +} +.mixin (@a; @b: black) when (iscolor(@b)) { + /* ... */ +} + +.mixin (@a) when (@a > 0) { + /* ... */ +} +.mixin (@a) when (default()) { + /* ... */ +} // matches only if first mixin does not, i.e. when @a <= 0 + +.my-optional-style() when (@my-option = true) { + button { + color: white; + } +} +.my-optional-style(); + +button when (@my-option = true) { + color: white; +} + +& when (@my-option = true) { + button { + color: white; + } + a { + color: blue; + } +} + +.loop(@counter) when (@counter > 0) { + .loop((@counter - 1)); // next iteration + width: (10px * @counter); // code for each iteration +} + +div { + .loop(5); // launch the loop +} + +div { + width: 10px; + width: 20px; + width: 30px; + width: 40px; + width: 50px; +} + +.generate-columns(4); + +.generate-columns(@n, @i: 1) when (@i =< @n) { + .column-@{i} { + width: (@i * 100% / @n); + } + .generate-columns(@n, (@i + 1)); +} + +.column-1 { + width: 25%; +} +.column-2 { + width: 50%; +} +.column-3 { + width: 75%; +} +.column-4 { + width: 100%; +} + +.mixin() { + box-shadow+: inset 0 0 10px #555; +} +.myclass { + .mixin(); + box-shadow+: 0 0 20px black; +} + +.myclass { + box-shadow: inset 0 0 10px #555, 0 0 20px black; +} + +.mixin() { + transform+_: scale(2); +} +.myclass { + .mixin(); + transform+_: rotate(15deg); +} + +.myclass { + transform: scale(2) rotate(15deg); +} + +a { + color: blue; + &:hover { + color: green; + } +} + +a { + color: blue; +} + +a:hover { + color: green; +} + +.button { + &-ok { + background-image: url("ok.png"); + } + &-cancel { + background-image: url("cancel.png"); + } + + &-custom { + background-image: url("custom.png"); + } +} + +.button-ok { + background-image: url("ok.png"); +} +.button-cancel { + background-image: url("cancel.png"); +} +.button-custom { + background-image: url("custom.png"); +} + +.link { + & + & { + color: red; + } + + & & { + color: green; + } + + && { + color: blue; + } + + &, + &ish { + color: cyan; + } +} + +.link + .link { + color: red; +} +.link .link { + color: green; +} +.link.link { + color: blue; +} +.link, +.linkish { + color: cyan; +} + +.grand { + .parent { + & > & { + color: red; + } + + & & { + color: green; + } + + && { + color: blue; + } + + &, + &ish { + color: cyan; + } + } +} + +.grand .parent > .grand .parent { + color: red; +} +.grand .parent .grand .parent { + color: green; +} +.grand .parent.grand .parent { + color: blue; +} +.grand .parent, +.grand .parentish { + color: cyan; +} + +.header { + .menu { + border-radius: 5px; + .no-borderradius & { + background-image: url("images/button-background.png"); + } + } +} + +.header .menu { + border-radius: 5px; +} +.no-borderradius .header .menu { + background-image: url("images/button-background.png"); +} + +p, +a, +ul, +li { + border-top: 2px dotted #366; + & + & { + border-top: 0; + } +} + +p, +a, +ul, +li { + border-top: 2px dotted #366; +} +p + p, +p + a, +p + ul, +p + li, +a + p, +a + a, +a + ul, +a + li, +ul + p, +ul + a, +ul + ul, +ul + li, +li + p, +li + a, +li + ul, +li + li { + border-top: 0; +} + +`; diff --git a/tests/css_import/jsfmt.spec.js b/tests/css_less/jsfmt.spec.js similarity index 100% rename from tests/css_import/jsfmt.spec.js rename to tests/css_less/jsfmt.spec.js diff --git a/tests/css_less_docs/less_docs.css b/tests/css_less/less.css similarity index 95% rename from tests/css_less_docs/less_docs.css rename to tests/css_less/less.css index 0229682b..d770158f 100644 --- a/tests/css_less_docs/less_docs.css +++ b/tests/css_less/less.css @@ -159,8 +159,8 @@ background-color: #112244 + #111; // result is #223355 background-color: white } } - .tab { ... } - .citation { ... } + .tab { /* ... */ } + .citation { /* ... */ } } @@ -663,9 +663,6 @@ link:hover:visited { } -
Bear - - .animal { background-color: black; color: white; @@ -674,10 +671,6 @@ link:hover:visited { background-color: brown; } - -Bear - - .animal { background-color: black; color: white; @@ -981,11 +974,11 @@ pre { } -.mixin(...) {} // matches 0-N arguments +.mixin(/* ... */) {} // matches 0-N arguments .mixin() {} // matches exactly 0 arguments .mixin(@a: 1) {} // matches 0-1 arguments -.mixin(@a: 1; ...) {} // matches 0-N arguments -.mixin(@a; ...) {} // matches 1-N arguments +.mixin(@a: 1; /* ... */) {} // matches 0-N arguments +.mixin(@a; /* ... */) {} // matches 1-N arguments .mixin(@a; @rest...) { @@ -994,7 +987,7 @@ pre { } -.mixin(@s; @color) { ... } +.mixin(@s; @color) { /* ... */ } .class { .mixin(@switch; #888); @@ -1250,9 +1243,6 @@ selector { } -ERROR 1:32 The variable "@one" was not declared. - - #space { .importer-1() { @detached: { scope-detached: @variable; }; // define detached ruleset @@ -1274,6 +1264,7 @@ ERROR 1:32 The variable "@one" was not declared. scope-detached: value; } +@my-ruleset:{.my-selector{@media tv{background-color:black;}}}; .foo { background: #900; @@ -1345,8 +1336,8 @@ ERROR 1:32 The variable "@one" was not declared. } -.truth (@a) when (@a) { ... } -.truth (@a) when (@a = true) { ... } +.truth (@a) when (@a) { /* ... */ } +.truth (@a) when (@a = true) { /* ... */ } .class { @@ -1356,28 +1347,28 @@ ERROR 1:32 The variable "@one" was not declared. @media: mobile; -.mixin (@a) when (@media = mobile) { ... } -.mixin (@a) when (@media = desktop) { ... } +.mixin (@a) when (@media = mobile) { /* ... */ } +.mixin (@a) when (@media = desktop) { /* ... */ } .max (@a; @b) when (@a > @b) { width: @a } .max (@a; @b) when (@a < @b) { width: @b } -.mixin (@a) when (isnumber(@a)) and (@a > 0) { ... } +.mixin (@a) when (isnumber(@a)) and (@a > 0) { /* ... */ } -.mixin (@a) when (@a > 10), (@a < -10) { ... } +.mixin (@a) when (@a > 10), (@a < -10) { /* ... */ } -.mixin (@b) when not (@b > 0) { ... } +.mixin (@b) when not (@b > 0) { /* ... */ } -.mixin (@a; @b: 0) when (isnumber(@b)) { ... } -.mixin (@a; @b: black) when (iscolor(@b)) { ... } +.mixin (@a; @b: 0) when (isnumber(@b)) { /* ... */ } +.mixin (@a; @b: black) when (iscolor(@b)) { /* ... */ } -.mixin (@a) when (@a > 0) { ... } -.mixin (@a) when (default()) { ... } // matches only if first mixin does not, i.e. when @a <= 0 +.mixin (@a) when (@a > 0) { /* ... */ } +.mixin (@a) when (default()) { /* ... */ } // matches only if first mixin does not, i.e. when @a <= 0 .my-optional-style() when (@my-option = true) { diff --git a/tests/css_media/__snapshots__/jsfmt.spec.js.snap b/tests/css_media/__snapshots__/jsfmt.spec.js.snap deleted file mode 100644 index 4398cc98..00000000 --- a/tests/css_media/__snapshots__/jsfmt.spec.js.snap +++ /dev/null @@ -1,13 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`overflow.css 1`] = ` -@media all and (-webkit-min-device-pixel-ratio: 1.5), all and (-o-min-device-pixel-ratio: 3/2), all and (min--moz-device-pixel-ratio: 1.5), all and (min-device-pixel-ratio: 1.5) { -} -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -@media all and (-webkit-min-device-pixel-ratio: 1.5), - all and (-o-min-device-pixel-ratio: 3/2), - all and (min--moz-device-pixel-ratio: 1.5), - all and (min-device-pixel-ratio: 1.5) { -} - -`; diff --git a/tests/css_media/overflow.css b/tests/css_media/overflow.css deleted file mode 100644 index edd11eb4..00000000 --- a/tests/css_media/overflow.css +++ /dev/null @@ -1,2 +0,0 @@ -@media all and (-webkit-min-device-pixel-ratio: 1.5), all and (-o-min-device-pixel-ratio: 3/2), all and (min--moz-device-pixel-ratio: 1.5), all and (min-device-pixel-ratio: 1.5) { -} diff --git a/tests/css_modules/__snapshots__/jsfmt.spec.js.snap b/tests/css_modules/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..2dc310de --- /dev/null +++ b/tests/css_modules/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,98 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`modules.css 1`] = ` +@value colors: './colors.css'; +@value first, second, third from colors; + +.title { + @nest :global(h1)& { + background: red; + } +} + +.className { + color: green; + background: red; +} + +.otherClassName { + composes: className; + color: yellow; +} + +.otherClassName { + composes: className from "./style.css"; +} + +.otherClassName { + composes: globalClassName from global; +} + +:global { + .global-class-name { + color: green; + } +} + +.root :global .text { + color: brown; + font-size: 24px; + font-family: helvetica, arial, sans-serif; + font-weight: 600; +} + +:import("path/to/dep.css") { + localAlias: keyFromDep; +} +:export { + exportedKey: exportedValue; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +@value colors: './colors.css'; +@value first, second, third from colors; + +.title { + @nest :global(h1)& { + background: red; + } +} + +.className { + color: green; + background: red; +} + +.otherClassName { + composes: className; + color: yellow; +} + +.otherClassName { + composes: className from "./style.css"; +} + +.otherClassName { + composes: globalClassName from global; +} + +:global { + .global-class-name { + color: green; + } +} + +.root :global .text { + color: brown; + font-size: 24px; + font-family: helvetica, arial, sans-serif; + font-weight: 600; +} + +:import("path/to/dep.css") { + localAlias: keyFromDep; +} +:export { + exportedKey: exportedValue; +} + +`; diff --git a/tests/css_media/jsfmt.spec.js b/tests/css_modules/jsfmt.spec.js similarity index 100% rename from tests/css_media/jsfmt.spec.js rename to tests/css_modules/jsfmt.spec.js diff --git a/tests/css_modules/modules.css b/tests/css_modules/modules.css new file mode 100644 index 00000000..bd857430 --- /dev/null +++ b/tests/css_modules/modules.css @@ -0,0 +1,46 @@ +@value colors: './colors.css'; +@value first, second, third from colors; + +.title { + @nest :global(h1)& { + background: red; + } +} + +.className { + color: green; + background: red; +} + +.otherClassName { + composes: className; + color: yellow; +} + +.otherClassName { + composes: className from "./style.css"; +} + +.otherClassName { + composes: globalClassName from global; +} + +:global { + .global-class-name { + color: green; + } +} + +.root :global .text { + color: brown; + font-size: 24px; + font-family: helvetica, arial, sans-serif; + font-weight: 600; +} + +:import("path/to/dep.css") { + localAlias: keyFromDep; +} +:export { + exportedKey: exportedValue; +} diff --git a/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap b/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap index 35b20b3c..0333e156 100644 --- a/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_numbers/__snapshots__/jsfmt.spec.js.snap @@ -68,8 +68,12 @@ exports[`numbers.css 1`] = ` a: 10; a: 9700; a: 10e100; + height: attr(data-size em, .01); } } + +@media only screen and (-webkit-min-device-pixel-ratio : .5), only screen and (min-device-pixel-ratio : .5) { } +@include single-transition(transform, .5s ease); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @supports (margin: 0.5px ".30px" 1e2px) { a { @@ -138,7 +142,13 @@ exports[`numbers.css 1`] = ` a: 10; a: 9700; a: 10e100; + height: attr(data-size em, 0.01); } } +@media only screen and (-webkit-min-device-pixel-ratio: 0.5), + only screen and (min-device-pixel-ratio: 0.5) { +} +@include single-transition(transform, 0.5s ease); + `; diff --git a/tests/css_numbers/numbers.css b/tests/css_numbers/numbers.css index c71b7b30..ab78d3e4 100644 --- a/tests/css_numbers/numbers.css +++ b/tests/css_numbers/numbers.css @@ -65,5 +65,9 @@ a: 10; a: 9700; a: 10e100; + height: attr(data-size em, .01); } } + +@media only screen and (-webkit-min-device-pixel-ratio : .5), only screen and (min-device-pixel-ratio : .5) { } +@include single-transition(transform, .5s ease); diff --git a/tests/css_parens/__snapshots__/jsfmt.spec.js.snap b/tests/css_parens/__snapshots__/jsfmt.spec.js.snap index 016a0e1f..d9b74ad0 100644 --- a/tests/css_parens/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_parens/__snapshots__/jsfmt.spec.js.snap @@ -1,6 +1,108 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`parens.js 1`] = ` +exports[`parens.css 1`] = ` +a { + prop1: func(1px,1px,1px,func(1px,1px,1px,func(1px,1px,1px))); + prop2: func( 1px ,1px ,1px ,func( 1px ,1px ,1px ,func( 1px ,1px ,1px ) ) ); + prop3: func(1px, 1px, 1px, func(1px, 1px, 1px, func(1px, 1px, 1px))); + prop4: func( 1px , 1px , 1px , func( 1px , 1px , 1px , func( 1px , 1px , 1px ) ) ); + prop5: func( 1px , 1px , 1px , func( 1px , 1px , 1px , func( 1px , 1px , 1px ) ) ); + prop6: func( + 1px + , + 1px + , + 1px + , + func( + 1px + , + 1px + , + 1px + , + func( + 1px + , + 1px + , + 1px + ) + ) + ); + prop7: + func( + 1px + , + 1px + , + 1px + , + func( + 1px + , + 1px + , + 1px + , + func( + 1px + , + 1px + , + 1px + ) + ) + ); + prop8: very-very-very-very-very-very-vey-very-very-long-func(1px,1px,1px,very-very-very-very-very-very-vey-very-very-long-func(1px,1px,1px,very-very-very-very-very-very-vey-very-very-long-func(1px,1px,1px))); + prop9: very-very-very-very-very-very-vey-very-very-long-func( 1px , 1px , 1px , very-very-very-very-very-very-vey-very-very-long-func( 1px , 1px , 1px , very-very-very-very-very-very-vey-very-very-long-func( 1px , 1px , 1px ) ) ); + prop10: + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px + , + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px, + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px + ) + ) + ); + prop11: very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px + , + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px, + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px + ) + ) + ); +} + a { box-shadow: inset 0 $size $size (-$size) black; width: calc(100% - (#{var(--g-spacing)} - #{$iframe-x-padding}) * 2); @@ -11,15 +113,407 @@ a { $image-height / ($image-width-responsive + $image-margin-responsive * 2) ); } + +.foo { + --paddingC: calc( var( --widthB ) / 2 ); + content: attr( data-title ); + color: var( --main-bg-color ); + background-color: rgb( 255, 0, 0 ); + background: element( #css-source ); + width: calc( 100% - ( #{var(--g-spacing)} - #{$iframe-x-padding} ) * 2 ); + padding-bottom: + ( + 100% * + $image-height / ( $image-width-responsive + $image-margin-responsive * 2 ) + ); + padding-top: var( --paddingC ); + margin: 1*1 (1)*1 1*(1) (1)*(1); + prop: -1*-1 -(-1)*-1 -1*-(-1) -(-1)*-(-1); + prop1: #{($m)*(10)}; + prop2: #{$m * 10}; + prop3: #{-(-$m)*-(-10)}; + prop4: +1; + prop5: -1; + prop6: word + 1; /* word1 */ + prop7: word - 1; /* word-1 */ + prop8: +1 +1 +1 +1; /* +1 +1 +1 +1 */ + prop9: -1 -1 -1 -1; /* -1 -1 -1 -1 */ + prop10: (-1); + prop11: (+1); + prop12: 10px/8px; + prop13: round(1.5)/2 round(1.5) /2 round(1.5)/ 2 round(1.5) / 2; + prop14: 2/round(1.5) 2 /round(1.5) 2/ round(1.5) 2 / round(1.5); + prop15: (round(1.5)/2) (round(1.5) /2) (round(1.5)/ 2) (round(1.5) / 2); + prop16: (2/round(1.5)) (2 /round(1.5)) (2/ round(1.5)) (2 / round(1.5)); + prop17: $width/2 $width /2 $width/ 2 $width / 2; + prop18: 2/$width 2 /$width 2/ $width 2 / $width; + prop19: ($width/2) ($width /2) ($width/ 2) ($width / 2); + prop20: (2/$width) (2 /$width) (2/ $width) (2 / $width); + prop21: @width/2 @width /2 @width/ 2 @width / 2; + prop22: 2/@width 2 /@width 2/ @width 2 / @width; + prop23: (@width/2) (@width /2) (@width/ 2) (@width / 2); + prop24: (2/@width) (2 /@width) (2/ @width) (2 / @width); + prop25-1: #{$width}/#{$width} #{$width} /#{$width} #{$width}/ #{$width} #{$width} / #{$width}; + prop25-2: #{$width}*#{$width} #{$width} *#{$width} #{$width}* #{$width} #{$width} * #{$width}; + prop25-3: #{$width}+#{$width} #{$width} +#{$width} #{$width}+ #{$width} #{$width} + #{$width}; + prop25-4: #{$width}-#{$width} #{$width} -#{$width} #{$width}- #{$width} #{$width} - #{$width}; + prop26: 8px/2px 8px /1 1/ 2px 1 / 2; + prop27: 8px/2px 8px/1 1/2px 1/2; + prop28: 8px / 2px 8px / 1 1 / 2px 1 / 2; + prop29: (8px/2px) (8px/1) (1/2px) (1/2); + prop30: (8px / 2px) (8px / 1) (1 / 2px) (1 / 2); + prop31: (#{$width}/2px) (8px/#{$width}) (#{$width} / 2px) (8px / #{$width}); + prop32: func(8px/2); + prop33: 5px + 8px/2px; + prop34: func(+20px, + 20px); + prop35: 1+1+1+1; + prop36: 1 + 1 + 1 + 1; + prop37: 1 +1 1 +1; + prop38: ++1; + prop39: ++(1); + prop40: --1; + prop41: --(1); + prop42: 1px+1px+1px+1px; + prop43: 1px + 1px + 1px + 1px; + prop44: -1+-1 -(-1)+-1 -1+-(-1) -(-1)+-(-1); + prop45: round(1.5)*2 round(1.5) *2 round(1.5)* 2 round(1.5) * 2; + prop46: 2*round(1.5) 2 *round(1.5) 2* round(1.5) 2 * round(1.5); + prop47: (round(1.5)*2) (round(1.5) *2) (round(1.5)* 2) (round(1.5) * 2); + prop48: (2*round(1.5)) (2 *round(1.5)) (2* round(1.5)) (2 * round(1.5)); + prop49: $width*2 $width *2 $width* 2 $width * 2; + prop50: 2*$width 2 *$width 2* $width 2 * $width; + prop51: ($width*2) ($width *2) ($width* 2) ($width * 2); + prop52: (2*$width) (2 *$width) (2* $width) (2 * $width); + prop53: @width*2 @width *2 @width* 2 @width * 2; + prop54: 2*@width 2 *@width 2* @width 2 * @width; + prop55: (@width*2) (@width *2) (@width* 2) (@width * 2); + prop56: (2*@width) (2 *@width) (2* @width) (2 * @width); + prop57: round(1.5)+2 round(1.5) +2 round(1.5)+ 2 round(1.5) + 2; + prop58: 2+round(1.5) 2 +round(1.5) 2+ round(1.5) 2 + round(1.5); + prop59: (round(1.5)+2) (round(1.5) +2) (round(1.5)+ 2) (round(1.5) + 2); + prop60: (2+round(1.5)) (2 +round(1.5)) (2+ round(1.5)) (2 + round(1.5)); + prop61: $width+2 $width +2 $width+ 2 $width + 2; + prop62: 2+$width 2 +$width 2+ $width 2 + $width; + prop63: ($width+2) ($width +2) ($width+ 2) ($width + 2); + prop64: (2+$width) (2 +$width) (2+ $width) (2 + $width); + prop65: @width+2 @width +2 @width+ 2 @width + 2; + prop66: 2+@width 2 +@width 2+ @width 2 + @width; + prop67: (@width+2) (@width +2) (@width+ 2) (@width + 2); + prop68: (2+@width) (2 +@width) (2+ @width) (2 + @width); + prop69: "test"+1 "test" +1 "test"+ 1 "test" + 1; + prop70: 1+"test" 1 +"test" 1+ "test" 1 + "test"; + prop71: "test"-1 "test" -1 "test"- 1 "test" - 1; + prop72: 1-"test" 1 -"test" 1- "test" 1 - "test"; + prop73: calc(100%*2px) calc(100% *2px) calc(100%* 2px) calc(100% * 2px); + prop74: calc(100%/2px) calc(100% /2px) calc(100%/ 2px) calc(100% / 2px); + prop75: calc(100%+2px) calc(100% +2px) calc(100%+ 2px) calc(100% + 2px); + prop76: calc(100%-2px) calc(100% -2px) calc(100%- 2px) calc(100% - 2px); + prop77: calc(-5px); + prop78: calc(+5px); + prop79: calc(-100px + 100px); + prop80: calc(+100px + 100px); + prop81: calc(100px - 100px); + prop82: calc(100px + 100px); +} + +.bar { + prop1: attr(data-foo); + prop2: attr( data-foo ); + prop3: attr( data-foo ); + prop4: attr( + data-foo + ); + prop5: attr( + data-foo + ); + prop6: + attr(data-foo); + prop7: + attr( + data-foo + ); + prop8: + attr( + data-foo + ) + ; + prop9:"("attr(title)")"; + prop10: "(" attr( title ) ")"; + prop11: "(" attr( title ) ")"; + prop12: "(" + attr(title) + ")"; + prop13: + "(" + attr(title) + ")"; + prop14: + "(" + attr( + title + ) + ")"; + prop15: + "(" + attr( + title + ) + ")" + ; + prop16: attr(length em); + prop17: attr(length em); + prop18: attr(length em); + prop19: attr( + length + em + ); + prop20:attr(data-size em,20); + prop21: attr( data-size em , 20 ); + prop22: attr( data-size em , 20 ); + prop23: attr( + data-size em, + 20 + ); + prop24: + attr( + data-size em, + 20 + ); + prop25: + attr( + data-size + em, + 20 + ) + ; +} + +.bar { + filter: progid:DXImageTransform.Microsoft.gradient(enabled='false',startColorstr='#fff',endColorstr='#000'); + filter: progid:DXImageTransform.Microsoft.Shadow(color='#042b47', Direction=45, Strength=6) progid:DXImageTransform.Microsoft.Shadow(color='#042b47', Direction=135, Strength=6); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#fad59f, endColorstr=#fa9907)"; +} + +.unicode-ranges +{ + /* values */ + unicode-range: U+26; /* single codepoint */ + unicode-range: U+0-7F; + unicode-range: U+0025-00FF; /* codepoint range */ + unicode-range: U+4??; /* wildcard range */ + unicode-range: U+0025-00FF, U+4??; /* multiple values */ +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +a { + prop1: func(1px, 1px, 1px, func(1px, 1px, 1px, func(1px, 1px, 1px))); + prop2: func(1px, 1px, 1px, func(1px, 1px, 1px, func(1px, 1px, 1px))); + prop3: func(1px, 1px, 1px, func(1px, 1px, 1px, func(1px, 1px, 1px))); + prop4: func(1px, 1px, 1px, func(1px, 1px, 1px, func(1px, 1px, 1px))); + prop5: func(1px, 1px, 1px, func(1px, 1px, 1px, func(1px, 1px, 1px))); + prop6: func(1px, 1px, 1px, func(1px, 1px, 1px, func(1px, 1px, 1px))); + prop7: func(1px, 1px, 1px, func(1px, 1px, 1px, func(1px, 1px, 1px))); + prop8: very-very-very-very-very-very-vey-very-very-long-func( + 1px, + 1px, + 1px, + very-very-very-very-very-very-vey-very-very-long-func( + 1px, + 1px, + 1px, + very-very-very-very-very-very-vey-very-very-long-func(1px, 1px, 1px) + ) + ); + prop9: very-very-very-very-very-very-vey-very-very-long-func( + 1px, + 1px, + 1px, + very-very-very-very-very-very-vey-very-very-long-func( + 1px, + 1px, + 1px, + very-very-very-very-very-very-vey-very-very-long-func(1px, 1px, 1px) + ) + ); + prop10: very-very-very-very-very-very-vey-very-very-long-func( + 1px, + 1px, + 1px, + very-very-very-very-very-very-vey-very-very-long-func( + 1px, + 1px, + 1px, + very-very-very-very-very-very-vey-very-very-long-func(1px, 1px, 1px) + ) + ); + prop11: very-very-very-very-very-very-vey-very-very-long-func( + 1px, + 1px, + 1px, + very-very-very-very-very-very-vey-very-very-long-func( + 1px, + 1px, + 1px, + very-very-very-very-very-very-vey-very-very-long-func(1px, 1px, 1px) + ) + ); +} + a { box-shadow: inset 0 $size $size (-$size) black; width: calc(100% - (#{var(--g-spacing)} - #{$iframe-x-padding}) * 2); padding-right: (100% * $info-width / (1 - $image-width)); padding-bottom: ( - 100% * $image-height / ($image-width-responsive + $image-margin-responsive * - 2) + 100% * $image-height / + ($image-width-responsive + $image-margin-responsive * 2) ); } +.foo { + --paddingC: calc(var(--widthB) / 2); + content: attr(data-title); + color: var(--main-bg-color); + background-color: rgb(255, 0, 0); + background: element(#css-source); + width: calc(100% - (#{var(--g-spacing)} - #{$iframe-x-padding}) * 2); + padding-bottom: ( + 100% * $image-height / + ($image-width-responsive + $image-margin-responsive * 2) + ); + padding-top: var(--paddingC); + margin: 1 * 1 (1) * 1 1 * (1) (1) * (1); + prop: -1 * -1 - (-1) * -1 -1 * -(-1) - (-1) * -(-1); + prop1: #{($m) * (10)}; + prop2: #{$m * 10}; + prop3: #{- (-$m) * -(-10)}; + prop4: +1; + prop5: -1; + prop6: word + 1; /* word1 */ + prop7: word - 1; /* word-1 */ + prop8: +1 +1 +1 +1; /* +1 +1 +1 +1 */ + prop9: -1 -1 -1 -1; /* -1 -1 -1 -1 */ + prop10: (-1); + prop11: (+1); + prop12: 10px/8px; + prop13: round(1.5) / 2 round(1.5) / 2 round(1.5) / 2 round(1.5) / 2; + prop14: 2 / round(1.5) 2 / round(1.5) 2 / round(1.5) 2 / round(1.5); + prop15: (round(1.5) / 2) (round(1.5) / 2) (round(1.5) / 2) (round(1.5) / 2); + prop16: (2 / round(1.5)) (2 / round(1.5)) (2 / round(1.5)) (2 / round(1.5)); + prop17: $width/2 $width / 2 $width/ 2 $width / 2; + prop18: 2 / $width 2 / $width 2 / $width 2 / $width; + prop19: ($width/2) ($width / 2) ($width/ 2) ($width / 2); + prop20: (2 / $width) (2 / $width) (2 / $width) (2 / $width); + prop21: @width / 2 @width / 2 @width / 2 @width / 2; + prop22: 2 / @width 2 / @width 2 / @width 2 / @width; + prop23: (@width / 2) (@width / 2) (@width / 2) (@width / 2); + prop24: (2 / @width) (2 / @width) (2 / @width) (2 / @width); + prop25-1: #{$width}/#{$width} #{$width} /#{$width} #{$width}/ #{$width} #{$width} / + #{$width}; + prop25-2: #{$width}*#{$width} #{$width} *#{$width} #{$width}* #{$width} #{$width} * + #{$width}; + prop25-3: #{$width}+#{$width} #{$width} +#{$width} #{$width}+ #{$width} #{$width} + + #{$width}; + prop25-4: #{$width}-#{$width} #{$width} -#{$width} #{$width}- #{$width} #{$width} - + #{$width}; + prop26: 8px/2px 8px /1 1/ 2px 1 / 2; + prop27: 8px/2px 8px/1 1/2px 1/2; + prop28: 8px / 2px 8px / 1 1 / 2px 1 / 2; + prop29: (8px/2px) (8px/1) (1/2px) (1/2); + prop30: (8px / 2px) (8px / 1) (1 / 2px) (1 / 2); + prop31: (#{$width}/ 2px) (8px /#{$width}) (#{$width} / 2px) (8px / #{$width}); + prop32: func(8px/2); + prop33: 5px + 8px/2px; + prop34: func(+20px, +20px); + prop35: 1+1+1+1; + prop36: 1 + 1 + 1 + 1; + prop37: 1 +1 1 +1; + prop38: ++1; + prop39: ++(1); + prop40: --1; + prop41: --(1); + prop42: 1px+1px+1px+1px; + prop43: 1px + 1px + 1px + 1px; + prop44: -1+-1 - (-1)+-1 -1+-(-1) - (-1)+-(-1); + prop45: round(1.5) * 2 round(1.5) * 2 round(1.5) * 2 round(1.5) * 2; + prop46: 2 * round(1.5) 2 * round(1.5) 2 * round(1.5) 2 * round(1.5); + prop47: (round(1.5) * 2) (round(1.5) * 2) (round(1.5) * 2) (round(1.5) * 2); + prop48: (2 * round(1.5)) (2 * round(1.5)) (2 * round(1.5)) (2 * round(1.5)); + prop49: $width * 2 $width * 2 $width * 2 $width * 2; + prop50: 2 * $width 2 * $width 2 * $width 2 * $width; + prop51: ($width * 2) ($width * 2) ($width * 2) ($width * 2); + prop52: (2 * $width) (2 * $width) (2 * $width) (2 * $width); + prop53: @width*2 @width * 2 @width* 2 @width * 2; + prop54: 2 * @width 2 * @width 2 * @width 2 * @width; + prop55: (@width*2) (@width * 2) (@width* 2) (@width * 2); + prop56: (2 * @width) (2 * @width) (2 * @width) (2 * @width); + prop57: round(1.5) + 2 round(1.5) + 2 round(1.5) + 2 round(1.5) + 2; + prop58: 2 + round(1.5) 2 + round(1.5) 2 + round(1.5) 2 + round(1.5); + prop59: (round(1.5) + 2) (round(1.5) + 2) (round(1.5) + 2) (round(1.5) + 2); + prop60: (2 + round(1.5)) (2 + round(1.5)) (2 + round(1.5)) (2 + round(1.5)); + prop61: $width + 2 $width + 2 $width + 2 $width + 2; + prop62: 2 + $width 2 + $width 2 + $width 2 + $width; + prop63: ($width + 2) ($width + 2) ($width + 2) ($width + 2); + prop64: (2 + $width) (2 + $width) (2 + $width) (2 + $width); + prop65: @width+2 @width + 2 @width+ 2 @width + 2; + prop66: 2 + @width 2 + @width 2 + @width 2 + @width; + prop67: (@width+2) (@width + 2) (@width+ 2) (@width + 2); + prop68: (2 + @width) (2 + @width) (2 + @width) (2 + @width); + prop69: "test"+1 "test" +1 "test"+ 1 "test" + 1; + prop70: 1+"test" 1 +"test" 1+ "test" 1 + "test"; + prop71: "test"-1 "test" -1 "test"- 1 "test" - 1; + prop72: 1-"test" 1 -"test" 1- "test" 1 - "test"; + prop73: calc(100% * 2px) calc(100% * 2px) calc(100% * 2px) calc(100% * 2px); + prop74: calc(100% / 2px) calc(100% / 2px) calc(100% / 2px) calc(100% / 2px); + prop75: calc(100%+2px) calc(100% +2px) calc(100%+ 2px) calc(100% + 2px); + prop76: calc(100%-2px) calc(100% -2px) calc(100%- 2px) calc(100% - 2px); + prop77: calc(-5px); + prop78: calc(+5px); + prop79: calc(-100px + 100px); + prop80: calc(+100px + 100px); + prop81: calc(100px - 100px); + prop82: calc(100px + 100px); +} + +.bar { + prop1: attr(data-foo); + prop2: attr(data-foo); + prop3: attr(data-foo); + prop4: attr(data-foo); + prop5: attr(data-foo); + prop6: attr(data-foo); + prop7: attr(data-foo); + prop8: attr(data-foo); + prop9: "(" attr(title) ")"; + prop10: "(" attr(title) ")"; + prop11: "(" attr(title) ")"; + prop12: "(" attr(title) ")"; + prop13: "(" attr(title) ")"; + prop14: "(" attr(title) ")"; + prop15: "(" attr(title) ")"; + prop16: attr(length em); + prop17: attr(length em); + prop18: attr(length em); + prop19: attr(length em); + prop20: attr(data-size em, 20); + prop21: attr(data-size em, 20); + prop22: attr(data-size em, 20); + prop23: attr(data-size em, 20); + prop24: attr(data-size em, 20); + prop25: attr(data-size em, 20); +} + +.bar { + filter: progid:DXImageTransform.Microsoft.gradient(enabled='false',startColorstr='#fff',endColorstr='#000'); + filter: progid:DXImageTransform.Microsoft.Shadow(color='#042b47', Direction=45, Strength=6) progid:DXImageTransform.Microsoft.Shadow(color='#042b47', Direction=135, Strength=6); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#fad59f, endColorstr=#fa9907)"; +} + +.unicode-ranges { + /* values */ + unicode-range: U+26; /* single codepoint */ + unicode-range: U+0-7F; + unicode-range: U+0025-00FF; /* codepoint range */ + unicode-range: U+4??; /* wildcard range */ + unicode-range: U+0025-00FF, U+4??; /* multiple values */ +} + `; diff --git a/tests/css_parens/parens.css b/tests/css_parens/parens.css new file mode 100644 index 00000000..73d9554f --- /dev/null +++ b/tests/css_parens/parens.css @@ -0,0 +1,302 @@ +a { + prop1: func(1px,1px,1px,func(1px,1px,1px,func(1px,1px,1px))); + prop2: func( 1px ,1px ,1px ,func( 1px ,1px ,1px ,func( 1px ,1px ,1px ) ) ); + prop3: func(1px, 1px, 1px, func(1px, 1px, 1px, func(1px, 1px, 1px))); + prop4: func( 1px , 1px , 1px , func( 1px , 1px , 1px , func( 1px , 1px , 1px ) ) ); + prop5: func( 1px , 1px , 1px , func( 1px , 1px , 1px , func( 1px , 1px , 1px ) ) ); + prop6: func( + 1px + , + 1px + , + 1px + , + func( + 1px + , + 1px + , + 1px + , + func( + 1px + , + 1px + , + 1px + ) + ) + ); + prop7: + func( + 1px + , + 1px + , + 1px + , + func( + 1px + , + 1px + , + 1px + , + func( + 1px + , + 1px + , + 1px + ) + ) + ); + prop8: very-very-very-very-very-very-vey-very-very-long-func(1px,1px,1px,very-very-very-very-very-very-vey-very-very-long-func(1px,1px,1px,very-very-very-very-very-very-vey-very-very-long-func(1px,1px,1px))); + prop9: very-very-very-very-very-very-vey-very-very-long-func( 1px , 1px , 1px , very-very-very-very-very-very-vey-very-very-long-func( 1px , 1px , 1px , very-very-very-very-very-very-vey-very-very-long-func( 1px , 1px , 1px ) ) ); + prop10: + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px + , + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px, + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px + ) + ) + ); + prop11: very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px + , + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px, + very-very-very-very-very-very-vey-very-very-long-func( + 1px + , + 1px + , + 1px + ) + ) + ); +} + +a { + box-shadow: inset 0 $size $size (-$size) black; + width: calc(100% - (#{var(--g-spacing)} - #{$iframe-x-padding}) * 2); + padding-right: (100% * $info-width / (1 - $image-width)); + padding-bottom: + ( + 100% * + $image-height / ($image-width-responsive + $image-margin-responsive * 2) + ); +} + +.foo { + --paddingC: calc( var( --widthB ) / 2 ); + content: attr( data-title ); + color: var( --main-bg-color ); + background-color: rgb( 255, 0, 0 ); + background: element( #css-source ); + width: calc( 100% - ( #{var(--g-spacing)} - #{$iframe-x-padding} ) * 2 ); + padding-bottom: + ( + 100% * + $image-height / ( $image-width-responsive + $image-margin-responsive * 2 ) + ); + padding-top: var( --paddingC ); + margin: 1*1 (1)*1 1*(1) (1)*(1); + prop: -1*-1 -(-1)*-1 -1*-(-1) -(-1)*-(-1); + prop1: #{($m)*(10)}; + prop2: #{$m * 10}; + prop3: #{-(-$m)*-(-10)}; + prop4: +1; + prop5: -1; + prop6: word + 1; /* word1 */ + prop7: word - 1; /* word-1 */ + prop8: +1 +1 +1 +1; /* +1 +1 +1 +1 */ + prop9: -1 -1 -1 -1; /* -1 -1 -1 -1 */ + prop10: (-1); + prop11: (+1); + prop12: 10px/8px; + prop13: round(1.5)/2 round(1.5) /2 round(1.5)/ 2 round(1.5) / 2; + prop14: 2/round(1.5) 2 /round(1.5) 2/ round(1.5) 2 / round(1.5); + prop15: (round(1.5)/2) (round(1.5) /2) (round(1.5)/ 2) (round(1.5) / 2); + prop16: (2/round(1.5)) (2 /round(1.5)) (2/ round(1.5)) (2 / round(1.5)); + prop17: $width/2 $width /2 $width/ 2 $width / 2; + prop18: 2/$width 2 /$width 2/ $width 2 / $width; + prop19: ($width/2) ($width /2) ($width/ 2) ($width / 2); + prop20: (2/$width) (2 /$width) (2/ $width) (2 / $width); + prop21: @width/2 @width /2 @width/ 2 @width / 2; + prop22: 2/@width 2 /@width 2/ @width 2 / @width; + prop23: (@width/2) (@width /2) (@width/ 2) (@width / 2); + prop24: (2/@width) (2 /@width) (2/ @width) (2 / @width); + prop25-1: #{$width}/#{$width} #{$width} /#{$width} #{$width}/ #{$width} #{$width} / #{$width}; + prop25-2: #{$width}*#{$width} #{$width} *#{$width} #{$width}* #{$width} #{$width} * #{$width}; + prop25-3: #{$width}+#{$width} #{$width} +#{$width} #{$width}+ #{$width} #{$width} + #{$width}; + prop25-4: #{$width}-#{$width} #{$width} -#{$width} #{$width}- #{$width} #{$width} - #{$width}; + prop26: 8px/2px 8px /1 1/ 2px 1 / 2; + prop27: 8px/2px 8px/1 1/2px 1/2; + prop28: 8px / 2px 8px / 1 1 / 2px 1 / 2; + prop29: (8px/2px) (8px/1) (1/2px) (1/2); + prop30: (8px / 2px) (8px / 1) (1 / 2px) (1 / 2); + prop31: (#{$width}/2px) (8px/#{$width}) (#{$width} / 2px) (8px / #{$width}); + prop32: func(8px/2); + prop33: 5px + 8px/2px; + prop34: func(+20px, + 20px); + prop35: 1+1+1+1; + prop36: 1 + 1 + 1 + 1; + prop37: 1 +1 1 +1; + prop38: ++1; + prop39: ++(1); + prop40: --1; + prop41: --(1); + prop42: 1px+1px+1px+1px; + prop43: 1px + 1px + 1px + 1px; + prop44: -1+-1 -(-1)+-1 -1+-(-1) -(-1)+-(-1); + prop45: round(1.5)*2 round(1.5) *2 round(1.5)* 2 round(1.5) * 2; + prop46: 2*round(1.5) 2 *round(1.5) 2* round(1.5) 2 * round(1.5); + prop47: (round(1.5)*2) (round(1.5) *2) (round(1.5)* 2) (round(1.5) * 2); + prop48: (2*round(1.5)) (2 *round(1.5)) (2* round(1.5)) (2 * round(1.5)); + prop49: $width*2 $width *2 $width* 2 $width * 2; + prop50: 2*$width 2 *$width 2* $width 2 * $width; + prop51: ($width*2) ($width *2) ($width* 2) ($width * 2); + prop52: (2*$width) (2 *$width) (2* $width) (2 * $width); + prop53: @width*2 @width *2 @width* 2 @width * 2; + prop54: 2*@width 2 *@width 2* @width 2 * @width; + prop55: (@width*2) (@width *2) (@width* 2) (@width * 2); + prop56: (2*@width) (2 *@width) (2* @width) (2 * @width); + prop57: round(1.5)+2 round(1.5) +2 round(1.5)+ 2 round(1.5) + 2; + prop58: 2+round(1.5) 2 +round(1.5) 2+ round(1.5) 2 + round(1.5); + prop59: (round(1.5)+2) (round(1.5) +2) (round(1.5)+ 2) (round(1.5) + 2); + prop60: (2+round(1.5)) (2 +round(1.5)) (2+ round(1.5)) (2 + round(1.5)); + prop61: $width+2 $width +2 $width+ 2 $width + 2; + prop62: 2+$width 2 +$width 2+ $width 2 + $width; + prop63: ($width+2) ($width +2) ($width+ 2) ($width + 2); + prop64: (2+$width) (2 +$width) (2+ $width) (2 + $width); + prop65: @width+2 @width +2 @width+ 2 @width + 2; + prop66: 2+@width 2 +@width 2+ @width 2 + @width; + prop67: (@width+2) (@width +2) (@width+ 2) (@width + 2); + prop68: (2+@width) (2 +@width) (2+ @width) (2 + @width); + prop69: "test"+1 "test" +1 "test"+ 1 "test" + 1; + prop70: 1+"test" 1 +"test" 1+ "test" 1 + "test"; + prop71: "test"-1 "test" -1 "test"- 1 "test" - 1; + prop72: 1-"test" 1 -"test" 1- "test" 1 - "test"; + prop73: calc(100%*2px) calc(100% *2px) calc(100%* 2px) calc(100% * 2px); + prop74: calc(100%/2px) calc(100% /2px) calc(100%/ 2px) calc(100% / 2px); + prop75: calc(100%+2px) calc(100% +2px) calc(100%+ 2px) calc(100% + 2px); + prop76: calc(100%-2px) calc(100% -2px) calc(100%- 2px) calc(100% - 2px); + prop77: calc(-5px); + prop78: calc(+5px); + prop79: calc(-100px + 100px); + prop80: calc(+100px + 100px); + prop81: calc(100px - 100px); + prop82: calc(100px + 100px); +} + +.bar { + prop1: attr(data-foo); + prop2: attr( data-foo ); + prop3: attr( data-foo ); + prop4: attr( + data-foo + ); + prop5: attr( + data-foo + ); + prop6: + attr(data-foo); + prop7: + attr( + data-foo + ); + prop8: + attr( + data-foo + ) + ; + prop9:"("attr(title)")"; + prop10: "(" attr( title ) ")"; + prop11: "(" attr( title ) ")"; + prop12: "(" + attr(title) + ")"; + prop13: + "(" + attr(title) + ")"; + prop14: + "(" + attr( + title + ) + ")"; + prop15: + "(" + attr( + title + ) + ")" + ; + prop16: attr(length em); + prop17: attr(length em); + prop18: attr(length em); + prop19: attr( + length + em + ); + prop20:attr(data-size em,20); + prop21: attr( data-size em , 20 ); + prop22: attr( data-size em , 20 ); + prop23: attr( + data-size em, + 20 + ); + prop24: + attr( + data-size em, + 20 + ); + prop25: + attr( + data-size + em, + 20 + ) + ; +} + +.bar { + filter: progid:DXImageTransform.Microsoft.gradient(enabled='false',startColorstr='#fff',endColorstr='#000'); + filter: progid:DXImageTransform.Microsoft.Shadow(color='#042b47', Direction=45, Strength=6) progid:DXImageTransform.Microsoft.Shadow(color='#042b47', Direction=135, Strength=6); + -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr=#fad59f, endColorstr=#fa9907)"; +} + +.unicode-ranges +{ + /* values */ + unicode-range: U+26; /* single codepoint */ + unicode-range: U+0-7F; + unicode-range: U+0025-00FF; /* codepoint range */ + unicode-range: U+4??; /* wildcard range */ + unicode-range: U+0025-00FF, U+4??; /* multiple values */ +} diff --git a/tests/css_parens/parens.js b/tests/css_parens/parens.js deleted file mode 100644 index 4b3adb1d..00000000 --- a/tests/css_parens/parens.js +++ /dev/null @@ -1,10 +0,0 @@ -a { - box-shadow: inset 0 $size $size (-$size) black; - width: calc(100% - (#{var(--g-spacing)} - #{$iframe-x-padding}) * 2); - padding-right: (100% * $info-width / (1 - $image-width)); - padding-bottom: - ( - 100% * - $image-height / ($image-width-responsive + $image-margin-responsive * 2) - ); -} diff --git a/tests/css_postcss_plugins/__snapshots__/jsfmt.spec.js.snap b/tests/css_postcss_plugins/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..cd6544af --- /dev/null +++ b/tests/css_postcss_plugins/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,554 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`postcss-mixins.css 1`] = ` +a { + @mixin $(theme)-colors; +} + +@define-mixin icon $network, $color: blue { + .icon.is-$(network) { + color: $color; + @mixin-content; + } + .icon.is-$(network):hover { + color: white; + background: $color; + } +} + +@mixin icon twitter { + background: url(twt.png); +} +@mixin icon youtube, red { + background: url(youtube.png); +} + +.search { + @mixin icon search; +} + +a { + color: black; + @mixin parent { + @mixin child; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +a { + @mixin $(theme)-colors; +} + +@define-mixin icon $network, $color: blue { + .icon.is-$(network) { + color: $color; + @mixin-content; + } + .icon.is-$(network):hover { + color: white; + background: $color; + } +} + +@mixin icon twitter { + background: url(twt.png); +} +@mixin icon youtube, red { + background: url(youtube.png); +} + +.search { + @mixin icon search; +} + +a { + color: black; + @mixin parent { + @mixin child; + } +} + +`; + +exports[`postcss-nested.css 1`] = ` +.phone { + &_title { + width: 500px; + @media (max-width: 500px) { + width: auto; + } + body.is_dark & { + color: white; + } + } + img { + display: block; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.phone { + &_title { + width: 500px; + @media (max-width: 500px) { + width: auto; + } + body.is_dark & { + color: white; + } + } + img { + display: block; + } +} + +`; + +exports[`postcss-nested-props.css 1`] = ` +.funky { + font: { + family: fantasy; + size: 30em; + weight: bold; + } +} + +.funky { + font: 20px/24px fantasy { + weight: bold; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.funky { + font: { + family: fantasy; + size: 30em; + weight: bold; + } +} + +.funky { + font: 20px/24px fantasy { + weight: bold; + } +} + +`; + +exports[`postcss-nesting.css 1`] = ` +a { + order: 1; + @nest b & { + order: 2; + } + @nest c & { + order: 3; + } + @nest d & { + order: 4; + } + @nest e & { + order: 5; + } +} +a { + order: 1; + @nest & b { + order: 2; + } + @nest & c { + order: 3; + } + @nest & d { + order: 4; + } + @nest & e { + order: 5; + } +} + +.rule-1 { + order: 1; + @media screen, print { + order: 2; + &.rule-2 { + order: 3; + @media (max-width: 30em) { + order: 4; + @nest .rule-prefix & { + order: 5; + } + order: 6; + } + order: 7; + } + order: 8; + } + order: 9; +} + +a, b { + order: 1; + & c, & d { + order: 2; + & e, & f { + order: 3; + } + order: 4; + } + order: 5; +} +a, b { + order: 1; + @nest & c, & d { + order: 2; + @nest & e, & f { + order: 3; + } + order: 4; + } + order: 5; +} + +a { + & b { + & c { + order: 1; + } + } +} +d { + order: 2; + & e { + order: 3; + } +} +f { + & g { + order: 4; + } + order: 5; +} +a { + @nest & b { + @nest & c { + order: 1; + } + } +} +d { + order: 2; + @nest & e { + order: 3; + } +} +f { + @nest & g { + order: 4; + } + order: 5; +} + +a, b { + order: 1; + c, d { + order: 2; + } +} +& e { + order: 3; +} +f { + & g & { + order: 4; + } + &h { + order: 5; + } +} +a, b { + order: 1; + @nest c, d { + order: 2; + } +} +@nest & e { + order: 3; +} +f { + @nest & g & { + order: 4; + } + @nest &h { + order: 5; + } +} + +a { + order: 1; + @media (min-width: 100px) { + order: 2; + @media (max-width: 200px) { + order: 3; + } + & b { + @media (max-width: 200px) { + order: 4; + } + } + } + @media screen, print and speech { + @media (max-width: 300px), (min-aspect-ratio: 16/9) { + order: 5; + & c { + order: 6; + } + } + } +} +a { + order: 1; + @media (min-width: 100px) { + order: 2; + @media (max-width: 200px) { + order: 3; + } + @nest & b { + @media (max-width: 200px) { + order: 4; + } + } + } + @media screen, print and speech { + @media (max-width: 300px), (min-aspect-ratio: 16/9) { + order: 5; + @nest & c { + order: 6; + } + } + } +} +a { + order: 1; + @nest very-very-very-very-very-very-very-very-very-long-selector &, very-very-very-very-very-very-very-very-very-long-selector & { + order: 2; + } + @nest very-very-very-very-very-very-very-very-very-long-selector + very-very-very-very-very-very-very-very-very-long-selector &, very-very-very-very-very-very-very-very-very-long-selector very-very-very-very-very-very-very-very-very-long-selector & { + order: 2; + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +a { + order: 1; + @nest b & { + order: 2; + } + @nest c & { + order: 3; + } + @nest d & { + order: 4; + } + @nest e & { + order: 5; + } +} +a { + order: 1; + @nest & b { + order: 2; + } + @nest & c { + order: 3; + } + @nest & d { + order: 4; + } + @nest & e { + order: 5; + } +} + +.rule-1 { + order: 1; + @media screen, print { + order: 2; + &.rule-2 { + order: 3; + @media (max-width: 30em) { + order: 4; + @nest .rule-prefix & { + order: 5; + } + order: 6; + } + order: 7; + } + order: 8; + } + order: 9; +} + +a, +b { + order: 1; + & c, + & d { + order: 2; + & e, + & f { + order: 3; + } + order: 4; + } + order: 5; +} +a, +b { + order: 1; + @nest & c, & d { + order: 2; + @nest & e, & f { + order: 3; + } + order: 4; + } + order: 5; +} + +a { + & b { + & c { + order: 1; + } + } +} +d { + order: 2; + & e { + order: 3; + } +} +f { + & g { + order: 4; + } + order: 5; +} +a { + @nest & b { + @nest & c { + order: 1; + } + } +} +d { + order: 2; + @nest & e { + order: 3; + } +} +f { + @nest & g { + order: 4; + } + order: 5; +} + +a, +b { + order: 1; + c, + d { + order: 2; + } +} +& e { + order: 3; +} +f { + & g & { + order: 4; + } + &h { + order: 5; + } +} +a, +b { + order: 1; + @nest c, d { + order: 2; + } +} +@nest & e { + order: 3; +} +f { + @nest & g & { + order: 4; + } + @nest &h { + order: 5; + } +} + +a { + order: 1; + @media (min-width: 100px) { + order: 2; + @media (max-width: 200px) { + order: 3; + } + & b { + @media (max-width: 200px) { + order: 4; + } + } + } + @media screen, print and speech { + @media (max-width: 300px), (min-aspect-ratio: 16/9) { + order: 5; + & c { + order: 6; + } + } + } +} +a { + order: 1; + @media (min-width: 100px) { + order: 2; + @media (max-width: 200px) { + order: 3; + } + @nest & b { + @media (max-width: 200px) { + order: 4; + } + } + } + @media screen, print and speech { + @media (max-width: 300px), (min-aspect-ratio: 16/9) { + order: 5; + @nest & c { + order: 6; + } + } + } +} +a { + order: 1; + @nest very-very-very-very-very-very-very-very-very-long-selector &, + very-very-very-very-very-very-very-very-very-long-selector & { + order: 2; + } + @nest very-very-very-very-very-very-very-very-very-long-selector + + very-very-very-very-very-very-very-very-very-long-selector + &, + very-very-very-very-very-very-very-very-very-long-selector + very-very-very-very-very-very-very-very-very-long-selector + & { + order: 2; + } +} + +`; diff --git a/tests/css_postcss_plugins/jsfmt.spec.js b/tests/css_postcss_plugins/jsfmt.spec.js new file mode 100644 index 00000000..7d3726c8 --- /dev/null +++ b/tests/css_postcss_plugins/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["css"]); diff --git a/tests/css_postcss_plugins/postcss-mixins.css b/tests/css_postcss_plugins/postcss-mixins.css new file mode 100644 index 00000000..495c8002 --- /dev/null +++ b/tests/css_postcss_plugins/postcss-mixins.css @@ -0,0 +1,32 @@ +a { + @mixin $(theme)-colors; +} + +@define-mixin icon $network, $color: blue { + .icon.is-$(network) { + color: $color; + @mixin-content; + } + .icon.is-$(network):hover { + color: white; + background: $color; + } +} + +@mixin icon twitter { + background: url(twt.png); +} +@mixin icon youtube, red { + background: url(youtube.png); +} + +.search { + @mixin icon search; +} + +a { + color: black; + @mixin parent { + @mixin child; + } +} diff --git a/tests/css_postcss_plugins/postcss-nested-props.css b/tests/css_postcss_plugins/postcss-nested-props.css new file mode 100644 index 00000000..655f481f --- /dev/null +++ b/tests/css_postcss_plugins/postcss-nested-props.css @@ -0,0 +1,13 @@ +.funky { + font: { + family: fantasy; + size: 30em; + weight: bold; + } +} + +.funky { + font: 20px/24px fantasy { + weight: bold; + } +} diff --git a/tests/css_postcss_plugins/postcss-nested.css b/tests/css_postcss_plugins/postcss-nested.css new file mode 100644 index 00000000..acdfa922 --- /dev/null +++ b/tests/css_postcss_plugins/postcss-nested.css @@ -0,0 +1,14 @@ +.phone { + &_title { + width: 500px; + @media (max-width: 500px) { + width: auto; + } + body.is_dark & { + color: white; + } + } + img { + display: block; + } +} diff --git a/tests/css_postcss_plugins/postcss-nesting.css b/tests/css_postcss_plugins/postcss-nesting.css new file mode 100644 index 00000000..34a44b76 --- /dev/null +++ b/tests/css_postcss_plugins/postcss-nesting.css @@ -0,0 +1,201 @@ +a { + order: 1; + @nest b & { + order: 2; + } + @nest c & { + order: 3; + } + @nest d & { + order: 4; + } + @nest e & { + order: 5; + } +} +a { + order: 1; + @nest & b { + order: 2; + } + @nest & c { + order: 3; + } + @nest & d { + order: 4; + } + @nest & e { + order: 5; + } +} + +.rule-1 { + order: 1; + @media screen, print { + order: 2; + &.rule-2 { + order: 3; + @media (max-width: 30em) { + order: 4; + @nest .rule-prefix & { + order: 5; + } + order: 6; + } + order: 7; + } + order: 8; + } + order: 9; +} + +a, b { + order: 1; + & c, & d { + order: 2; + & e, & f { + order: 3; + } + order: 4; + } + order: 5; +} +a, b { + order: 1; + @nest & c, & d { + order: 2; + @nest & e, & f { + order: 3; + } + order: 4; + } + order: 5; +} + +a { + & b { + & c { + order: 1; + } + } +} +d { + order: 2; + & e { + order: 3; + } +} +f { + & g { + order: 4; + } + order: 5; +} +a { + @nest & b { + @nest & c { + order: 1; + } + } +} +d { + order: 2; + @nest & e { + order: 3; + } +} +f { + @nest & g { + order: 4; + } + order: 5; +} + +a, b { + order: 1; + c, d { + order: 2; + } +} +& e { + order: 3; +} +f { + & g & { + order: 4; + } + &h { + order: 5; + } +} +a, b { + order: 1; + @nest c, d { + order: 2; + } +} +@nest & e { + order: 3; +} +f { + @nest & g & { + order: 4; + } + @nest &h { + order: 5; + } +} + +a { + order: 1; + @media (min-width: 100px) { + order: 2; + @media (max-width: 200px) { + order: 3; + } + & b { + @media (max-width: 200px) { + order: 4; + } + } + } + @media screen, print and speech { + @media (max-width: 300px), (min-aspect-ratio: 16/9) { + order: 5; + & c { + order: 6; + } + } + } +} +a { + order: 1; + @media (min-width: 100px) { + order: 2; + @media (max-width: 200px) { + order: 3; + } + @nest & b { + @media (max-width: 200px) { + order: 4; + } + } + } + @media screen, print and speech { + @media (max-width: 300px), (min-aspect-ratio: 16/9) { + order: 5; + @nest & c { + order: 6; + } + } + } +} +a { + order: 1; + @nest very-very-very-very-very-very-very-very-very-long-selector &, very-very-very-very-very-very-very-very-very-long-selector & { + order: 2; + } + @nest very-very-very-very-very-very-very-very-very-long-selector + very-very-very-very-very-very-very-very-very-long-selector &, very-very-very-very-very-very-very-very-very-long-selector very-very-very-very-very-very-very-very-very-long-selector & { + order: 2; + } +} diff --git a/tests/css_quotes/__snapshots__/jsfmt.spec.js.snap b/tests/css_quotes/__snapshots__/jsfmt.spec.js.snap index c648791d..4f557987 100644 --- a/tests/css_quotes/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_quotes/__snapshots__/jsfmt.spec.js.snap @@ -4,7 +4,6 @@ exports[`quotes.css 1`] = ` @charset "UTF-8"; /* @charset must always have double quotes: https://www.w3.org/TR/css-syntax-3/#determine-the-fallback-encoding */ /* Also, it has to be the very first thing in the file, but here are some more tests anyway: */ -@CHARSET "UTF-8"; /* Case insensitive for "consistency". */ @charset 'UTF-8'; /* Single quotes are invalid here. Keep them since we don't know what the user is doing. */ @supports (content: one "two" three 'four') { @@ -97,7 +96,6 @@ one 'two' three {} @charset "UTF-8"; /* @charset must always have double quotes: https://www.w3.org/TR/css-syntax-3/#determine-the-fallback-encoding */ /* Also, it has to be the very first thing in the file, but here are some more tests anyway: */ -@charset "UTF-8"; /* Case insensitive for "consistency". */ @charset 'UTF-8'; /* Single quotes are invalid here. Keep them since we don't know what the user is doing. */ @supports (content: one "two" three "four") { @@ -163,8 +161,8 @@ one 'two' three {} content: 'var backslash = "\\\\", doubleQuote = \\'"\\';'; /* Leave all "escapes" alone. */ - content: "\\Abc4 foo \\n" "\\end"; - content: "\\Abc4 foo \\n" "\\end"; + content: "\\Abc4 foo \\n" /* "comment" */ "\\end"; + content: "\\Abc4 foo \\n" /* 'comment' */ "\\end"; } } @@ -178,11 +176,11 @@ one 'two' three {} @import "foo.css" screen and (orientation: landscape); @foo "one"; -@foo "one"; -@foo "one" two "three"; +@foo 'one'; +@foo "one" two 'three'; @foo ("one"); -@foo ("one"); -@foo ("one" two "three"); +@foo ('one'); +@foo ("one" two 'three'); one "two" three { } @@ -195,7 +193,6 @@ exports[`quotes.css 2`] = ` @charset "UTF-8"; /* @charset must always have double quotes: https://www.w3.org/TR/css-syntax-3/#determine-the-fallback-encoding */ /* Also, it has to be the very first thing in the file, but here are some more tests anyway: */ -@CHARSET "UTF-8"; /* Case insensitive for "consistency". */ @charset 'UTF-8'; /* Single quotes are invalid here. Keep them since we don't know what the user is doing. */ @supports (content: one "two" three 'four') { @@ -288,7 +285,6 @@ one 'two' three {} @charset "UTF-8"; /* @charset must always have double quotes: https://www.w3.org/TR/css-syntax-3/#determine-the-fallback-encoding */ /* Also, it has to be the very first thing in the file, but here are some more tests anyway: */ -@charset "UTF-8"; /* Case insensitive for "consistency". */ @charset 'UTF-8'; /* Single quotes are invalid here. Keep them since we don't know what the user is doing. */ @supports (content: one 'two' three 'four') { @@ -354,8 +350,8 @@ one 'two' three {} content: 'var backslash = "\\\\", doubleQuote = \\'"\\';'; /* Leave all "escapes" alone. */ - content: '\\Abc4 foo \\n' '\\end'; - content: '\\Abc4 foo \\n' '\\end'; + content: '\\Abc4 foo \\n' /* "comment" */ '\\end'; + content: '\\Abc4 foo \\n' /* 'comment' */ '\\end'; } } @@ -368,12 +364,12 @@ one 'two' three {} @import 'foo.css' screen and (orientation: landscape); @import 'foo.css' screen and (orientation: landscape); +@foo "one"; @foo 'one'; -@foo 'one'; -@foo 'one' two 'three'; +@foo "one" two 'three'; +@foo ("one"); @foo ('one'); -@foo ('one'); -@foo ('one' two 'three'); +@foo ("one" two 'three'); one 'two' three { } diff --git a/tests/css_quotes/quotes.css b/tests/css_quotes/quotes.css index 2313eb12..33448fcc 100644 --- a/tests/css_quotes/quotes.css +++ b/tests/css_quotes/quotes.css @@ -1,7 +1,6 @@ @charset "UTF-8"; /* @charset must always have double quotes: https://www.w3.org/TR/css-syntax-3/#determine-the-fallback-encoding */ /* Also, it has to be the very first thing in the file, but here are some more tests anyway: */ -@CHARSET "UTF-8"; /* Case insensitive for "consistency". */ @charset 'UTF-8'; /* Single quotes are invalid here. Keep them since we don't know what the user is doing. */ @supports (content: one "two" three 'four') { diff --git a/tests/css_scss/__snapshots__/jsfmt.spec.js.snap b/tests/css_scss/__snapshots__/jsfmt.spec.js.snap index f0175c57..6ab0c825 100644 --- a/tests/css_scss/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_scss/__snapshots__/jsfmt.spec.js.snap @@ -18,7 +18,872 @@ div { } } a { margin: 0 { left: 10px; } } -$map: (color: #111111, text-shadow: 1px 1px 0 salmon); + +$default: #111111 !default; +$default: #111111 !default; +$default: #111111 +!default; +$default: "very-long-long-long-long-long-long-long-long-long-long-long-value" !default; +$default: "very-long-long-long-long-long-long-long-long-long-long-long-value" !default; +$default: "very-long-long-long-long-long-long-long-long-long-long-long-value" +!default; + +$global: #111111 !global; +$global: #111111 !global; +$global: #111111 +!global; +$global: "very-long-long-long-long-long-long-long-long-long-long-long-value" !global; +$global: "very-long-long-long-long-long-long-long-long-long-long-long-value" !global; +$global: "very-long-long-long-long-long-long-long-long-long-long-long-value" +!global; + +$map: (key: value, other-key: other-value); +$map: (key: value, other-key: other-value) !default; +$map: (key: value, other-key: other-value) !default; +$map: (key: value, other-key: other-value) +!default; +$map: +(key: value, other-key: other-value) +!default; +$map: ( key : value , other-key : other-value); +$map: ( key : value , other-key : other-value ); +$map: ( + key: value, + other-key: other-value +); +$map: ( +key: value, +other-key: other-value +); +$map: ( +key +: +value, +other-key +: +other-value +); +$map: ( +key +: +value +, +other-key +: +other-value +); +$map: (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value); +$map: ( very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key : very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value , very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key : very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value ); +$map: ( very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key : very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value , very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value ); +$map: ( + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: +( +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key +: +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value +, +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key +: +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( + key: (#d82d2d, #666), + other-key: (#52bf4a, #fff), + other-other-key: (#c23435, #fff) +); +$map: ( +key: (#d82d2d, #666), +other-key: (#52bf4a, #fff), +other-other-key: (#c23435, #fff) +); +$map: ( + key : ( #d82d2d , #666 ), + other-key : ( #52bf4a , #fff ), + other-other-key : ( #c23435 , #fff ) +); +$map: ( + key : ( #d82d2d , #666 ) , + other-key : ( #52bf4a , #fff ), + other-other-key : ( #c23435 , #fff ) +); +$map: ( +key +: +( +#d82d2d, +#666 +) +, +other-key +: +( +#52bf4a, +#fff +) +, +other-other-key +: +( +#c23435 +, +#fff +) +); +$map: map-merge($map, ($key: $value)); +$map: map-merge( $map , ( $key : $value ) ); +$map: map-merge( $map , ( $key : $value ) ); +$map: map-merge( + $map, + ($key: $value) +); +$map: map-merge( +$map, +($key: $value) +); +$map: +map-merge( +$map +, +( +$key +: +$value +) +); + +$longVariable: ( +(mobile $mobile) (tablet $tablet) (desktop $desktop) (wide $wide) +); + +$list-space: "item-1" "item-2" "item-3"; +$list-space:"item-1""item-2""item-3"; +$list-space: "item-1" "item-2" "item-3" ; +$list-space: "item-1" + "item-2" + "item-3"; +$list-space +: +"item-1" +"item-2" +"item-3" +; +$list-space + +: + +"item-1" + +"item-2" + +"item-3" + +; +$list-comma: "item-1", "item-2", "item-3"; +$list-comma:"item-1","item-2","item-3"; +$list-comma: "item-1" , "item-2" , "item-3" ; +$list-comma: "item-1", + "item-2", + "item-3"; +$list-comma +: +"item-1" +, +"item-2" +, +"item-3" +; +$list-comma + +: + +"item-1" + +, + +"item-2" + +, + +"item-3" + +; +$list: "item-1.1" "item-1.2" "item-1.3", "item-2.1" "item-2.2" "item-2.3", "item-3.1" "item-3.2" "item-3.3"; +$list:"item-1.1""item-1.2""item-1.3","item-2.1""item-2.2""item-2.3","item-3.1""item-3.2""item-3.3"; +$list: "item-1.1" "item-1.2" "item-1.3" , "item-2.1" "item-2.2" "item-2.3" , "item-3.1" "item-3.2" "item-3.3" ; +$list: "item-1.1" "item-1.2" "item-1.3", + "item-2.1" "item-2.2" "item-2.3", + "item-3.1" "item-3.2" "item-3.3"; +$list +: +"item-1.1" +"item-1.2" +"item-1.3" +, +"item-2.1" +"item-2.2" +"item-2.3" +, +"item-3.1" +"item-3.2" +"item-3.3" +; +$list + +: + +"item-1.1" + +"item-1.2" + +"item-1.3" + +, + +"item-2.1" + +"item-2.2" + +"item-2.3" + +, + +"item-3.1" + +"item-3.2" + +"item-3.3" + +; +$list: (("item-1.1", "item-1.2", "item-1.3"), ("item-2.1", "item-2.2", "item-2.3"), ("item-3.1", "item-3.2", "item-3.3")); +$list:(("item-1.1","item-1.2","item-1.3"),("item-2.1","item-2.2","item-2.3"),("item-3.1","item-3.2","item-3.3")); +$list: ( ( "item-1.1" , "item-1.2" , "item-1.3" ) , ( "item-2.1" , "item-2.2" , "item-2.3" ) , ( "item-3.1" , "item-3.2" , "item-3.3" ) ) ; +$list: ( + ("item-1.1", "item-1.2", "item-1.3"), + ("item-2.1", "item-2.2", "item-2.3"), + ("item-3.1", "item-3.2", "item-3.3") +); +$list +: +( +( +"item-1.1" +, +"item-1.2" +, +"item-1.3" +) +, +( +"item-2.1" +, +"item-2.2" +, +"item-2.3" +) +, +( +"item-3.1" +, +"item-3.2" +, +"item-3.3" +) +) +; +$list + +: + +( + +( + +"item-1.1" + +, + +"item-1.2" + +, + +"item-1.3" + +) + +, + +( + +"item-2.1" + +, + +"item-2.2" + +, + +"item-2.3" + +) + +, + +( + +"item-3.1" + +, + +"item-3.2" + +, + +"item-3.3" + +) + +) + +; + +$var: (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0); +$space-scale: (0, "0") (0.25, "0-25") (0.5, "0-5") (0.75, "0-75") (1, "1") (1.25, "1-25") (1.5, "1-5") (1.75, "1-75") (2, "2") (2.25, "2-25") (2.5, "2-5") (2.75, "2-75") (3, "3") (3.25, "3-25") (3.5, "3-5") (3.75, "3-75") (4, "4"); + +.card-column-simple { + @include breakpoint( getBp( md ) ) { + padding: $spacing_content-sm $spacing_content-md; + } + + @include breakpoint (getBp(md)) { + &:nth-child(2n + 3) { + clear: both; + } + } + + @include breakpoint (getBp(xl)) { + &:nth-child(2n + 3) { + clear: none; + } + &:nth-child(3n + 4) { + clear: both; + } + } +} + +@warn "Warn (#{$message})."; +@warn "Warn (#{$message})."; +@warn "Warn (#{$message})."; +@warn #{$message}; +@warn "Very long long long long long long long long long long long long long line (#{$message})."; +@warn + "Very long long long long long long long long long long long long long line (#{$message})."; +@error "Error (#{$message})."; +@error "Error (#{$message})."; +@error "Error (#{$message})."; +@error #{$message}; +@error "Very long long long long long long long long long long long long long line Error (#{$message})."; +@error + "Very long long long long long long long long long long long long long line Error (#{$message})."; + +$buttonConfig: "save" 50px, 'cancel' 50px, "help" 100PX; + +$locale: "en_us"; +html[lang=#{$locale}] { + font-size: 10px; +} +$alertClass: "error"; +p.message-#{$alertClass} { + color: red; +} +$mediumBreakpoint: 768px; +@media (max-width: #{$mediumBreakpoint}) { + a { + font-size: 18px; + } +} + +p { + @media (max-width: 768px) { + font-size: 150%; + + @media (orientation: landscape) { + line-height: 75%; + } + } +} + +.popularAnimal { + background: gray; +} +.GoodBoy { + color: green; +} +.dog { + @extend .popularAnimal; + @extend .GoodBoy; + color: white; +} + +%animal { + background: gray; +} +.cat { + @extend %animal; + color: white; +} +.dog { + @extend %animal; + color: black; +} + +%mfw-standing-out { + font-size: 150%; + font-style: italic; + padding: 25px; +} +%mfwSlightlyShadowed { + @include box-shadow(black 2px 2px 10px); // from Compass +} +%MFWRounded { + @include border-radius(25px); // from Compass +} +#join-button { + @extend %mfw-standing-out; + @extend %mfwSlightlyShadowed; + @extend %MFWRounded; + background: green; + color: white; +} + +a { + &:hover { + color: red; + } +} +p { + body.no-touch & { + display: none; + } +} +.foo.bar .baz.bang, .bip.qux { + $selector: &; +} +@mixin does-parent-exist { + @if & { + &:hover { + color: red; + } + } @else { + a { + color: red; + } + } +} + +p { + @if 1 + 1 == 2 { + border: 1px solid; + } + @if 5 < 3 { + border: 2px dotted; + } + @if null { + border: 3px double; + } +} + +$mosterType: monster; +p { + @if $mosterType == ocean { + color: blue; + } @else if $mosterType == matador { + color: red; + } @else if $mosterType == monster { + color: green; + } @else if $mosterType == nightKing { + color: green; + } @else if $mosterType == VeryWickedWolf { + color: green; + } @else { + color: black; + } +} + +@for $i from 1 through 3 { + .item-#{$i} { + width: 2em * $i; + } +} + +@each $animal in puma, sea-slug, cheerfulDog, BigSalamander, "string", 'another-string', "camelCaseString", "PascalCaseString" { + .#{$animal}-icon { + background-image: url('/images/#{$animal}.png'); + } +} + +$i: 6; +@while $i > 0 { + .item-#{$i} { + width: 2em * $i; + } + $i: $i - 2; +} + +@mixin cool-border($width: 10px, $coolStyle: 'solid', $AwesomeColor: "black") { + border: $width $coolStyle $AwesomeColor; +} + +p { + @include cool-border(1px, "solid", $fff); +} +p { + @include cool-border($width: 1px, $coolStyle: 'solid', $AwesomeColor: #fff); +} +p { + @include coolBorder(); +} + +@mixin coolBorder() { + border: 10px solid #fff; +} +p { + @include coolBorder(1px, "solid", $fff); +} + +@mixin CoolBorder() { + border: 10px solid #fff; +} +p { + @include CoolBorder(1px, "solid", $fff); +} + +@mixin box-shadow($shadows...) { + -moz-box-shadow: $shadows; + -webkit-box-shadow: $shadows; + box-shadow: $shadows; +} +.shadows { + @include box-shadow(0px 4px 5px #666, 2px 6px 10px #999); +} + +@mixin apply-to-ie6-only { + * html { + @content; + } +} +@include apply-to-ie6-only { + #logo { + background-image: url(/logo.gif); + } +} + +@mixin applyToIe6Only { + * html { + @content; + } +} +@include applyToIe6Only { + #logo { + background-image: url(/logo.gif); + } +} + +@mixin ApplyToIe6Only { + * html { + @content; + } +} +@include ApplyToIe6Only { + #logo { + background-image: url(/logo.gif); + } +} + +@mixin config-icon-colors($prefix, $colors...) { + @each $i in $colors { + .#{$prefix}#{nth($i, 1)} { + color: nth($i, 2); + } + } +} +@include config-icon-colors( + "icon-", + "save" green, + "cancel" gray, + "delete" red, + 'wait' blue +); + +@function my-calculation-function($some-number, $anotherNumber, $BigNumber){ + @return $some-number + $anotherNumber + $BigNumber; +} +@function myCalculationFunction($some-number, $anotherNumber, $BigNumber){ + @return $some-number + $anotherNumber + $BigNumber; +} +@function AnotherMyCalculationFunction($some-number, $anotherNumber, $BigNumber: 100px){ + @return $some-number + $anotherNumber + $BigNumber; +} +@function border($borders...) { + @return $borders; +} +.foo { + padding: my-calculation-function(10px, 5px, 100px); + margin: myCalculationFunction($some-number: 10px, $anotherNumber: 5px, $BigNumber: 100px); + width: AnotherMyCalculationFunction(10px, 5px); + border: border(25px, 35px); +} + +$sm-only: '(min-width: 768px) and (max-width: 991px)'; +$lg-and-up: '(min-width: 1200px)'; + +@media screen and #{$sm-only, $lg-and-up} { + color: #000; +} + +.class-#{$var} { + #{$var}: #7b3d66; + #{$attr}-color: blue; + #{$prop}-#{$side}: $value; + background-#{$var}: #7b3d66; + animation-name: #{var}; + line-height: #{strip-unit($line-height)}em; + height: 1#{$var}; + width: calc(100% - #{$sidebar-width}); + max-width: calc(#{$m*100}vw #{$sign} #{$b}); + font: #{$font-size}/#{$line-height}; + content: "I have #{8 + 2} books on SASS!"; + border: #{$var} #{$var} #{$var}; + filter: #{$var}#{$var}#{$var}; + prop: #{ $var + $var } #{ $var + $var } #{ $var + $var }; + prop2: + #{ + $var + + + $var + } + + #{ + $var + + + $var + } + + #{ + $var + + + $var + } + ; + prop3: + + #{ + + $var + + + + + $var + + } + + #{ + + $var + + + + + $var + + } + + #{ + + $var + + + + + $var + + } + ; + prop4: -#{$loader-icon-duration}; + prop5: +#{$loader-icon-duration}; + prop6: calc(-#{$loader-icon-duration} + 10); + prop7: calc(10 + -#{$loader-icon-duration}); +} + +/* Framework version for the generated CSS is #{$version}. */ + +.selector { + foo: bar; + #{$active} { + baz: qux; + } +} + +.el:nth-of-type(#{$i}) {} + +@media #{$value} {} + +@import url(#{$foundation-dir}/foundation/components/grid); + +@keyframes loader { + 0% { + transform: translate3d(0, 0, 0); + } + + #{50% - $loader-icon-duration} { + transform: translate3d(0, $bounce-height, 0); + } + + 50% { + transform: translate3d(0, $bounce-height, 0) scale($loader-bounce-horizontal-expansion, $loader-bounce-vertical-compression); + } +} + +$icons: wifi "\\600", wifi-hotspot "\\601", weather "\\602"; + +@each $icon in $icons { + .icon-#{nth($icon, 1)}, %icon-#{nth($icon, 1)} { + content: "#{nth($icon, 2)}"; + } +} + +.foo { + prop: -($grid-gutter-width / 2); + prop1: -( $grid-gutter-width / 2 ); + prop2: -$grid-gutter-width / 2; + prop3: +($grid-gutter-width / 2); + prop4: 10px/8px; /* Plain CSS, no division */ + prop5: $width / 2; /* Uses a variable, does division */ + prop6: round(1.5) / 2; /* Uses a function, does division */ + prop7: (500px / 2); /* Uses parentheses, does division */ + prop8: 5px + 8px / 2px; //* Uses +, does division */ + prop9: (italic bold 10px/8px); /* In a list, parentheses don't count */ + prop10: #010203 + #040506; + prop11: #010203 * 2; + prop12: rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75); + prop13: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}'); + prop14: e + -resize; + prop15: sans- + "serif"; + prop16: 1em + (2em * 3); + prop17: rotate(-2deg); + prop18: rotate( -2deg ) ; + _:_; + prop19: 10 - ($grid-gutter-width / 2); + prop20: 10 + -($grid-gutter-width / 2); + prop21: 10 + - ( $grid-gutter-width / 2 ) ; + prop22: - ( $grid-gutter-width / 2 ) ; + prop23: - ( $grid-gutter-width / 2 ) ; + prop24: -$grid-gutter-width; + prop25: + ( $grid-gutter-width / 2 ) ; + prop26: + ( $grid-gutter-width / 2 ) ; + prop27: +$grid-gutter-width; + prop28: --($grid-gutter-width / 2); + prop28: ++($grid-gutter-width / 2); + prop29: rotate( - 2deg ) ; +} + +$last:nth($juggler,length($juggler)); +$x:if($last%2==0,1/2,3/2); +$new:pow($last,$x); +$sequence:1,1 1,2 1,1 2 1 1, 1 1 1 2 2 1; +$new-entry:(); +$new-entry : ( ) ; +$new-entry : ( ) ; +$new-entry +: +( +) +; + +body:before { + content: quote(to-string(fibonacci(100), ' \\A ')); + white-space: pre-wrap; +} + +width: ((100% - (($numPerRow - 1) * $margin)) / $numPerRow); +width +: +( +( +100% +- +( +( +$numPerRow +- +1 +) +* +$margin +) +) +/ +$numPerRow +) +; + +a:nth-child(#{$numPerRow}n) { + margin-right: 0; + margin-bottom: 0; +} + +@function em($pixels, $context: $browser-context) { + @return #{ $pixels / $context }em +} + +.navigation { + @extend %updated-#{$flag}; + @extend .selected-#{$flag}; + @extend %#{$item}; +} + +.icon-#{$icon-name} { + background-image: '/images/#{$icon-name}.svg'; +} + +$extmods:(eot:"?",svg:"#" + str-replace($name," ","_")); + +@mixin keyframes {@-moz-keyframes{@content;}@-webkit-keyframes{@content;}} + +@function gcd($a,$b){ + // From: http://rosettacode.org/wiki/Greatest_common_divisor#JavaScript + @if ($b != 0) { + @return gcd($b,$a % $b); + }@else{ + @return abs($a); + } +} + +$colors: ( +primary: ( +base: #00abc9, +light: #daf1f6, +dark: #12799a +), +secondary: ( +base: #424d55, +light: #ccc, +lightest: #efefef, +dark: #404247 +), +success: ( +base: #bbd33e, +light: #eaf0c6 +) +); + +@function color($color, $tone: "base") { +@return map-get(map-get($colors, $color), $tone); +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @media #{$g-breakpoint-tiny} { } @@ -39,9 +904,668 @@ a { left: 10px; } } + +$default: #111111 !default; +$default: #111111 !default; +$default: #111111 !default; +$default: "very-long-long-long-long-long-long-long-long-long-long-long-value" !default; +$default: "very-long-long-long-long-long-long-long-long-long-long-long-value" !default; +$default: "very-long-long-long-long-long-long-long-long-long-long-long-value" !default; + +$global: #111111 !global; +$global: #111111 !global; +$global: #111111 !global; +$global: "very-long-long-long-long-long-long-long-long-long-long-long-value" !global; +$global: "very-long-long-long-long-long-long-long-long-long-long-long-value" !global; +$global: "very-long-long-long-long-long-long-long-long-long-long-long-value" !global; + $map: ( - color: #111111, - text-shadow: 1px 1px 0 salmon + key: value, + other-key: other-value +); +$map: ( + key: value, + other-key: other-value +) !default; +$map: ( + key: value, + other-key: other-value +) !default; +$map: ( + key: value, + other-key: other-value +) !default; +$map: ( + key: value, + other-key: other-value +) !default; +$map: ( + key: value, + other-key: other-value +); +$map: ( + key: value, + other-key: other-value +); +$map: ( + key: value, + other-key: other-value +); +$map: ( + key: value, + other-key: other-value +); +$map: ( + key: value, + other-key: other-value +); +$map: ( + key: value, + other-key: other-value +); +$map: ( + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( + key: (#d82d2d, #666), + other-key: (#52bf4a, #fff), + other-other-key: (#c23435, #fff) +); +$map: ( + key: (#d82d2d, #666), + other-key: (#52bf4a, #fff), + other-other-key: (#c23435, #fff) +); +$map: ( + key: (#d82d2d, #666), + other-key: (#52bf4a, #fff), + other-other-key: (#c23435, #fff) +); +$map: ( + key: (#d82d2d, #666), + other-key: (#52bf4a, #fff), + other-other-key: (#c23435, #fff) +); +$map: ( + key: (#d82d2d, #666), + other-key: (#52bf4a, #fff), + other-other-key: (#c23435, #fff) +); +$map: map-merge($map, ($key: $value)); +$map: map-merge($map, ($key: $value)); +$map: map-merge($map, ($key: $value)); +$map: map-merge($map, ($key: $value)); +$map: map-merge($map, ($key: $value)); +$map: map-merge($map, ($key: $value)); + +$longVariable: ( + (mobile $mobile) (tablet $tablet) (desktop $desktop) (wide $wide) ); +$list-space: "item-1" "item-2" "item-3"; +$list-space: "item-1" "item-2" "item-3"; +$list-space: "item-1" "item-2" "item-3"; +$list-space: "item-1" "item-2" "item-3"; +$list-space: "item-1" "item-2" "item-3"; +$list-space: "item-1" "item-2" "item-3"; +$list-comma: "item-1", "item-2", "item-3"; +$list-comma: "item-1", "item-2", "item-3"; +$list-comma: "item-1", "item-2", "item-3"; +$list-comma: "item-1", "item-2", "item-3"; +$list-comma: "item-1", "item-2", "item-3"; +$list-comma: "item-1", "item-2", "item-3"; +$list: "item-1.1" "item-1.2" "item-1.3", "item-2.1" "item-2.2" "item-2.3", + "item-3.1" "item-3.2" "item-3.3"; +$list: "item-1.1" "item-1.2" "item-1.3", "item-2.1" "item-2.2" "item-2.3", + "item-3.1" "item-3.2" "item-3.3"; +$list: "item-1.1" "item-1.2" "item-1.3", "item-2.1" "item-2.2" "item-2.3", + "item-3.1" "item-3.2" "item-3.3"; +$list: "item-1.1" "item-1.2" "item-1.3", "item-2.1" "item-2.2" "item-2.3", + "item-3.1" "item-3.2" "item-3.3"; +$list: "item-1.1" "item-1.2" "item-1.3", "item-2.1" "item-2.2" "item-2.3", + "item-3.1" "item-3.2" "item-3.3"; +$list: "item-1.1" "item-1.2" "item-1.3", "item-2.1" "item-2.2" "item-2.3", + "item-3.1" "item-3.2" "item-3.3"; +$list: ( + ("item-1.1", "item-1.2", "item-1.3"), + ("item-2.1", "item-2.2", "item-2.3"), + ("item-3.1", "item-3.2", "item-3.3") +); +$list: ( + ("item-1.1", "item-1.2", "item-1.3"), + ("item-2.1", "item-2.2", "item-2.3"), + ("item-3.1", "item-3.2", "item-3.3") +); +$list: ( + ("item-1.1", "item-1.2", "item-1.3"), + ("item-2.1", "item-2.2", "item-2.3"), + ("item-3.1", "item-3.2", "item-3.3") +); +$list: ( + ("item-1.1", "item-1.2", "item-1.3"), + ("item-2.1", "item-2.2", "item-2.3"), + ("item-3.1", "item-3.2", "item-3.3") +); +$list: ( + ("item-1.1", "item-1.2", "item-1.3"), + ("item-2.1", "item-2.2", "item-2.3"), + ("item-3.1", "item-3.2", "item-3.3") +); +$list: ( + ("item-1.1", "item-1.2", "item-1.3"), + ("item-2.1", "item-2.2", "item-2.3"), + ("item-3.1", "item-3.2", "item-3.3") +); + +$var: (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) + (0 0) (0 0); +$space-scale: (0, "0") (0.25, "0-25") (0.5, "0-5") (0.75, "0-75") (1, "1") + (1.25, "1-25") (1.5, "1-5") (1.75, "1-75") (2, "2") (2.25, "2-25") + (2.5, "2-5") (2.75, "2-75") (3, "3") (3.25, "3-25") (3.5, "3-5") + (3.75, "3-75") (4, "4"); + +.card-column-simple { + @include breakpoint(getBp(md)) { + padding: $spacing_content-sm $spacing_content-md; + } + + @include breakpoint(getBp(md)) { + &:nth-child(2n + 3) { + clear: both; + } + } + + @include breakpoint(getBp(xl)) { + &:nth-child(2n + 3) { + clear: none; + } + &:nth-child(3n + 4) { + clear: both; + } + } +} + +@warn "Warn (#{$message})."; +@warn "Warn (#{$message})."; +@warn "Warn (#{$message})."; +@warn #{$message}; +@warn "Very long long long long long long long long long long long long long line (#{$message})."; +@warn "Very long long long long long long long long long long long long long line (#{$message})."; +@error "Error (#{$message})."; +@error "Error (#{$message})."; +@error "Error (#{$message})."; +@error #{$message}; +@error "Very long long long long long long long long long long long long long line Error (#{$message})."; +@error "Very long long long long long long long long long long long long long line Error (#{$message})."; + +$buttonConfig: "save" 50px, "cancel" 50px, "help" 100px; + +$locale: "en_us"; +html[lang="#{$locale}"] { + font-size: 10px; +} +$alertClass: "error"; +p.message-#{$alertClass} { + color: red; +} +$mediumBreakpoint: 768px; +@media (max-width: #{$mediumBreakpoint}) { + a { + font-size: 18px; + } +} + +p { + @media (max-width: 768px) { + font-size: 150%; + + @media (orientation: landscape) { + line-height: 75%; + } + } +} + +.popularAnimal { + background: gray; +} +.GoodBoy { + color: green; +} +.dog { + @extend .popularAnimal; + @extend .GoodBoy; + color: white; +} + +%animal { + background: gray; +} +.cat { + @extend %animal; + color: white; +} +.dog { + @extend %animal; + color: black; +} + +%mfw-standing-out { + font-size: 150%; + font-style: italic; + padding: 25px; +} +%mfwSlightlyShadowed { + @include box-shadow(black 2px 2px 10px); // from Compass +} +%MFWRounded { + @include border-radius(25px); // from Compass +} +#join-button { + @extend %mfw-standing-out; + @extend %mfwSlightlyShadowed; + @extend %MFWRounded; + background: green; + color: white; +} + +a { + &:hover { + color: red; + } +} +p { + body.no-touch & { + display: none; + } +} +.foo.bar .baz.bang, +.bip.qux { + $selector: &; +} +@mixin does-parent-exist { + @if & { + &:hover { + color: red; + } + } @else { + a { + color: red; + } + } +} + +p { + @if 1 + 1 == 2 { + border: 1px solid; + } + @if 5 < 3 { + border: 2px dotted; + } + @if null { + border: 3px double; + } +} + +$mosterType: monster; +p { + @if $mosterType == ocean { + color: blue; + } @else if $mosterType == matador { + color: red; + } @else if $mosterType == monster { + color: green; + } @else if $mosterType == nightKing { + color: green; + } @else if $mosterType == VeryWickedWolf { + color: green; + } @else { + color: black; + } +} + +@for $i from 1 through 3 { + .item-#{$i} { + width: 2em * $i; + } +} + +@each $animal in puma, sea-slug, cheerfulDog, BigSalamander, "string", + "another-string", "camelCaseString", "PascalCaseString" +{ + .#{$animal}-icon { + background-image: url("/images/#{$animal}.png"); + } +} + +$i: 6; +@while $i > 0 { + .item-#{$i} { + width: 2em * $i; + } + $i: $i - 2; +} + +@mixin cool-border($width: 10px, $coolStyle: "solid", $AwesomeColor: "black") { + border: $width $coolStyle $AwesomeColor; +} + +p { + @include cool-border(1px, "solid", $fff); +} +p { + @include cool-border($width: 1px, $coolStyle: "solid", $AwesomeColor: #fff); +} +p { + @include coolBorder(); +} + +@mixin coolBorder() { + border: 10px solid #fff; +} +p { + @include coolBorder(1px, "solid", $fff); +} + +@mixin CoolBorder() { + border: 10px solid #fff; +} +p { + @include CoolBorder(1px, "solid", $fff); +} + +@mixin box-shadow($shadows...) { + -moz-box-shadow: $shadows; + -webkit-box-shadow: $shadows; + box-shadow: $shadows; +} +.shadows { + @include box-shadow(0px 4px 5px #666, 2px 6px 10px #999); +} + +@mixin apply-to-ie6-only { + * html { + @content; + } +} +@include apply-to-ie6-only { + #logo { + background-image: url(/logo.gif); + } +} + +@mixin applyToIe6Only { + * html { + @content; + } +} +@include applyToIe6Only { + #logo { + background-image: url(/logo.gif); + } +} + +@mixin ApplyToIe6Only { + * html { + @content; + } +} +@include ApplyToIe6Only { + #logo { + background-image: url(/logo.gif); + } +} + +@mixin config-icon-colors($prefix, $colors...) { + @each $i in $colors { + .#{$prefix}#{nth($i, 1)} { + color: nth($i, 2); + } + } +} +@include config-icon-colors( + "icon-", + "save" green, + "cancel" gray, + "delete" red, + "wait" blue +); + +@function my-calculation-function($some-number, $anotherNumber, $BigNumber) { + @return $some-number + $anotherNumber + $BigNumber; +} +@function myCalculationFunction($some-number, $anotherNumber, $BigNumber) { + @return $some-number + $anotherNumber + $BigNumber; +} +@function AnotherMyCalculationFunction( + $some-number, + $anotherNumber, + $BigNumber: 100px +) { + @return $some-number + $anotherNumber + $BigNumber; +} +@function border($borders...) { + @return $borders; +} +.foo { + padding: my-calculation-function(10px, 5px, 100px); + margin: myCalculationFunction( + $some-number: 10px, + $anotherNumber: 5px, + $BigNumber: 100px + ); + width: AnotherMyCalculationFunction(10px, 5px); + border: border(25px, 35px); +} + +$sm-only: "(min-width: 768px) and (max-width: 991px)"; +$lg-and-up: "(min-width: 1200px)"; + +@media screen and #{$sm-only, $lg-and-up} { + color: #000; +} + +.class-#{$var} { + #{$var}: #7b3d66; + #{$attr}-color: blue; + #{$prop}-#{$side}: $value; + background-#{$var}: #7b3d66; + animation-name: #{var}; + line-height: #{strip-unit($line-height)}em; + height: 1#{$var}; + width: calc(100% - #{$sidebar-width}); + max-width: calc(#{$m * 100}vw #{$sign} #{$b}); + font: #{$font-size}/#{$line-height}; + content: "I have #{8 + 2} books on SASS!"; + border: #{$var} #{$var} #{$var}; + filter: #{$var}#{$var}#{$var}; + prop: #{$var + $var} #{$var + $var} #{$var + $var}; + prop2: #{$var + $var} #{$var + $var} #{$var + $var}; + prop3: #{$var + $var} #{$var + $var} #{$var + $var}; + prop4: -#{$loader-icon-duration}; + prop5: +#{$loader-icon-duration}; + prop6: calc(-#{$loader-icon-duration} + 10); + prop7: calc(10 + -#{$loader-icon-duration}); +} + +/* Framework version for the generated CSS is #{$version}. */ + +.selector { + foo: bar; + #{$active} { + baz: qux; + } +} + +.el:nth-of-type(#{$i}) { +} + +@media #{$value} { +} + +@import url(#{$foundation-dir}/foundation/components/grid); + +@keyframes loader { + 0% { + transform: translate3d(0, 0, 0); + } + + #{50% - $loader-icon-duration} { + transform: translate3d(0, $bounce-height, 0); + } + + 50% { + transform: translate3d(0, $bounce-height, 0) + scale( + $loader-bounce-horizontal-expansion, + $loader-bounce-vertical-compression + ); + } +} + +$icons: wifi "\\600", wifi-hotspot "\\601", weather "\\602"; + +@each $icon in $icons { + .icon-#{nth($icon, 1)}, + %icon-#{nth($icon, 1)} { + content: "#{nth($icon, 2)}"; + } +} + +.foo { + prop: -($grid-gutter-width / 2); + prop1: -($grid-gutter-width / 2); + prop2: -$grid-gutter-width / 2; + prop3: +($grid-gutter-width / 2); + prop4: 10px/8px; /* Plain CSS, no division */ + prop5: $width / 2; /* Uses a variable, does division */ + prop6: round(1.5) / 2; /* Uses a function, does division */ + prop7: (500px / 2); /* Uses parentheses, does division */ + prop8: 5px + 8px / 2px; //* Uses +, does division */ + prop9: (italic bold 10px/8px); /* In a list, parentheses don't count */ + prop10: #010203 + #040506; + prop11: #010203 * 2; + prop12: rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75); + prop13: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}'); + prop14: e + -resize; + prop15: sans- + "serif"; + prop16: 1em + (2em * 3); + prop17: rotate(-2deg); + prop18: rotate(-2deg); + _: _; + prop19: 10 - ($grid-gutter-width / 2); + prop20: 10 + -($grid-gutter-width / 2); + prop21: 10 + -($grid-gutter-width / 2); + prop22: -($grid-gutter-width / 2); + prop23: -($grid-gutter-width / 2); + prop24: -$grid-gutter-width; + prop25: +($grid-gutter-width / 2); + prop26: +($grid-gutter-width / 2); + prop27: +$grid-gutter-width; + prop28: --($grid-gutter-width / 2); + prop28: ++($grid-gutter-width / 2); + prop29: rotate(-2deg); +} + +$last: nth($juggler, length($juggler)); +$x: if($last%2==0, 1/2, 3/2); +$new: pow($last, $x); +$sequence: 1, 1 1, 2 1, 1 2 1 1, 1 1 1 2 2 1; +$new-entry: (); +$new-entry: (); +$new-entry: (); +$new-entry: (); + +body:before { + content: quote(to-string(fibonacci(100), " \\A ")); + white-space: pre-wrap; +} + +width: ((100% - (($numPerRow - 1) * $margin)) / $numPerRow); +width: ((100% - (($numPerRow - 1) * $margin)) / $numPerRow); + +a:nth-child(#{$numPerRow}n) { + margin-right: 0; + margin-bottom: 0; +} + +@function em($pixels, $context: $browser-context) { + @return #{$pixels / $context}em; +} + +.navigation { + @extend %updated-#{$flag}; + @extend .selected-#{$flag}; + @extend %#{$item}; +} + +.icon-#{$icon-name} { + background-image: "/images/#{$icon-name}.svg"; +} + +$extmods: ( + eot: "?", + svg: "#" + str-replace($name, " ", "_") +); + +@mixin keyframes { + @-moz-keyframes { + @content; + } + @-webkit-keyframes { + @content; + } +} + +@function gcd($a, $b) { + // From: http://rosettacode.org/wiki/Greatest_common_divisor#JavaScript + @if ($b != 0) { + @return gcd($b, $a % $b); + } @else { + @return abs($a); + } +} + +$colors: ( + primary: (base: #00abc9, light: #daf1f6, dark: #12799a), + secondary: (base: #424d55, light: #ccc, lightest: #efefef, dark: #404247), + success: (base: #bbd33e, light: #eaf0c6) +); + +@function color($color, $tone: "base") { + @return map-get(map-get($colors, $color), $tone); +} + `; diff --git a/tests/css_scss/scss.css b/tests/css_scss/scss.css index f55a7f72..d599773c 100644 --- a/tests/css_scss/scss.css +++ b/tests/css_scss/scss.css @@ -8,4 +8,869 @@ div { } } a { margin: 0 { left: 10px; } } -$map: (color: #111111, text-shadow: 1px 1px 0 salmon); + +$default: #111111 !default; +$default: #111111 !default; +$default: #111111 +!default; +$default: "very-long-long-long-long-long-long-long-long-long-long-long-value" !default; +$default: "very-long-long-long-long-long-long-long-long-long-long-long-value" !default; +$default: "very-long-long-long-long-long-long-long-long-long-long-long-value" +!default; + +$global: #111111 !global; +$global: #111111 !global; +$global: #111111 +!global; +$global: "very-long-long-long-long-long-long-long-long-long-long-long-value" !global; +$global: "very-long-long-long-long-long-long-long-long-long-long-long-value" !global; +$global: "very-long-long-long-long-long-long-long-long-long-long-long-value" +!global; + +$map: (key: value, other-key: other-value); +$map: (key: value, other-key: other-value) !default; +$map: (key: value, other-key: other-value) !default; +$map: (key: value, other-key: other-value) +!default; +$map: +(key: value, other-key: other-value) +!default; +$map: ( key : value , other-key : other-value); +$map: ( key : value , other-key : other-value ); +$map: ( + key: value, + other-key: other-value +); +$map: ( +key: value, +other-key: other-value +); +$map: ( +key +: +value, +other-key +: +other-value +); +$map: ( +key +: +value +, +other-key +: +other-value +); +$map: (very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value); +$map: ( very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key : very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value , very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key : very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value ); +$map: ( very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key : very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value , very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value ); +$map: ( + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, + very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value, +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key: very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: +( +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-key +: +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-value +, +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-key +: +very-very-very-very-very-very-very-very-very-very-very-very-very-very-very-verylong-other-value +); +$map: ( + key: (#d82d2d, #666), + other-key: (#52bf4a, #fff), + other-other-key: (#c23435, #fff) +); +$map: ( +key: (#d82d2d, #666), +other-key: (#52bf4a, #fff), +other-other-key: (#c23435, #fff) +); +$map: ( + key : ( #d82d2d , #666 ), + other-key : ( #52bf4a , #fff ), + other-other-key : ( #c23435 , #fff ) +); +$map: ( + key : ( #d82d2d , #666 ) , + other-key : ( #52bf4a , #fff ), + other-other-key : ( #c23435 , #fff ) +); +$map: ( +key +: +( +#d82d2d, +#666 +) +, +other-key +: +( +#52bf4a, +#fff +) +, +other-other-key +: +( +#c23435 +, +#fff +) +); +$map: map-merge($map, ($key: $value)); +$map: map-merge( $map , ( $key : $value ) ); +$map: map-merge( $map , ( $key : $value ) ); +$map: map-merge( + $map, + ($key: $value) +); +$map: map-merge( +$map, +($key: $value) +); +$map: +map-merge( +$map +, +( +$key +: +$value +) +); + +$longVariable: ( +(mobile $mobile) (tablet $tablet) (desktop $desktop) (wide $wide) +); + +$list-space: "item-1" "item-2" "item-3"; +$list-space:"item-1""item-2""item-3"; +$list-space: "item-1" "item-2" "item-3" ; +$list-space: "item-1" + "item-2" + "item-3"; +$list-space +: +"item-1" +"item-2" +"item-3" +; +$list-space + +: + +"item-1" + +"item-2" + +"item-3" + +; +$list-comma: "item-1", "item-2", "item-3"; +$list-comma:"item-1","item-2","item-3"; +$list-comma: "item-1" , "item-2" , "item-3" ; +$list-comma: "item-1", + "item-2", + "item-3"; +$list-comma +: +"item-1" +, +"item-2" +, +"item-3" +; +$list-comma + +: + +"item-1" + +, + +"item-2" + +, + +"item-3" + +; +$list: "item-1.1" "item-1.2" "item-1.3", "item-2.1" "item-2.2" "item-2.3", "item-3.1" "item-3.2" "item-3.3"; +$list:"item-1.1""item-1.2""item-1.3","item-2.1""item-2.2""item-2.3","item-3.1""item-3.2""item-3.3"; +$list: "item-1.1" "item-1.2" "item-1.3" , "item-2.1" "item-2.2" "item-2.3" , "item-3.1" "item-3.2" "item-3.3" ; +$list: "item-1.1" "item-1.2" "item-1.3", + "item-2.1" "item-2.2" "item-2.3", + "item-3.1" "item-3.2" "item-3.3"; +$list +: +"item-1.1" +"item-1.2" +"item-1.3" +, +"item-2.1" +"item-2.2" +"item-2.3" +, +"item-3.1" +"item-3.2" +"item-3.3" +; +$list + +: + +"item-1.1" + +"item-1.2" + +"item-1.3" + +, + +"item-2.1" + +"item-2.2" + +"item-2.3" + +, + +"item-3.1" + +"item-3.2" + +"item-3.3" + +; +$list: (("item-1.1", "item-1.2", "item-1.3"), ("item-2.1", "item-2.2", "item-2.3"), ("item-3.1", "item-3.2", "item-3.3")); +$list:(("item-1.1","item-1.2","item-1.3"),("item-2.1","item-2.2","item-2.3"),("item-3.1","item-3.2","item-3.3")); +$list: ( ( "item-1.1" , "item-1.2" , "item-1.3" ) , ( "item-2.1" , "item-2.2" , "item-2.3" ) , ( "item-3.1" , "item-3.2" , "item-3.3" ) ) ; +$list: ( + ("item-1.1", "item-1.2", "item-1.3"), + ("item-2.1", "item-2.2", "item-2.3"), + ("item-3.1", "item-3.2", "item-3.3") +); +$list +: +( +( +"item-1.1" +, +"item-1.2" +, +"item-1.3" +) +, +( +"item-2.1" +, +"item-2.2" +, +"item-2.3" +) +, +( +"item-3.1" +, +"item-3.2" +, +"item-3.3" +) +) +; +$list + +: + +( + +( + +"item-1.1" + +, + +"item-1.2" + +, + +"item-1.3" + +) + +, + +( + +"item-2.1" + +, + +"item-2.2" + +, + +"item-2.3" + +) + +, + +( + +"item-3.1" + +, + +"item-3.2" + +, + +"item-3.3" + +) + +) + +; + +$var: (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0) (0 0); +$space-scale: (0, "0") (0.25, "0-25") (0.5, "0-5") (0.75, "0-75") (1, "1") (1.25, "1-25") (1.5, "1-5") (1.75, "1-75") (2, "2") (2.25, "2-25") (2.5, "2-5") (2.75, "2-75") (3, "3") (3.25, "3-25") (3.5, "3-5") (3.75, "3-75") (4, "4"); + +.card-column-simple { + @include breakpoint( getBp( md ) ) { + padding: $spacing_content-sm $spacing_content-md; + } + + @include breakpoint (getBp(md)) { + &:nth-child(2n + 3) { + clear: both; + } + } + + @include breakpoint (getBp(xl)) { + &:nth-child(2n + 3) { + clear: none; + } + &:nth-child(3n + 4) { + clear: both; + } + } +} + +@warn "Warn (#{$message})."; +@warn "Warn (#{$message})."; +@warn "Warn (#{$message})."; +@warn #{$message}; +@warn "Very long long long long long long long long long long long long long line (#{$message})."; +@warn + "Very long long long long long long long long long long long long long line (#{$message})."; +@error "Error (#{$message})."; +@error "Error (#{$message})."; +@error "Error (#{$message})."; +@error #{$message}; +@error "Very long long long long long long long long long long long long long line Error (#{$message})."; +@error + "Very long long long long long long long long long long long long long line Error (#{$message})."; + +$buttonConfig: "save" 50px, 'cancel' 50px, "help" 100PX; + +$locale: "en_us"; +html[lang=#{$locale}] { + font-size: 10px; +} +$alertClass: "error"; +p.message-#{$alertClass} { + color: red; +} +$mediumBreakpoint: 768px; +@media (max-width: #{$mediumBreakpoint}) { + a { + font-size: 18px; + } +} + +p { + @media (max-width: 768px) { + font-size: 150%; + + @media (orientation: landscape) { + line-height: 75%; + } + } +} + +.popularAnimal { + background: gray; +} +.GoodBoy { + color: green; +} +.dog { + @extend .popularAnimal; + @extend .GoodBoy; + color: white; +} + +%animal { + background: gray; +} +.cat { + @extend %animal; + color: white; +} +.dog { + @extend %animal; + color: black; +} + +%mfw-standing-out { + font-size: 150%; + font-style: italic; + padding: 25px; +} +%mfwSlightlyShadowed { + @include box-shadow(black 2px 2px 10px); // from Compass +} +%MFWRounded { + @include border-radius(25px); // from Compass +} +#join-button { + @extend %mfw-standing-out; + @extend %mfwSlightlyShadowed; + @extend %MFWRounded; + background: green; + color: white; +} + +a { + &:hover { + color: red; + } +} +p { + body.no-touch & { + display: none; + } +} +.foo.bar .baz.bang, .bip.qux { + $selector: &; +} +@mixin does-parent-exist { + @if & { + &:hover { + color: red; + } + } @else { + a { + color: red; + } + } +} + +p { + @if 1 + 1 == 2 { + border: 1px solid; + } + @if 5 < 3 { + border: 2px dotted; + } + @if null { + border: 3px double; + } +} + +$mosterType: monster; +p { + @if $mosterType == ocean { + color: blue; + } @else if $mosterType == matador { + color: red; + } @else if $mosterType == monster { + color: green; + } @else if $mosterType == nightKing { + color: green; + } @else if $mosterType == VeryWickedWolf { + color: green; + } @else { + color: black; + } +} + +@for $i from 1 through 3 { + .item-#{$i} { + width: 2em * $i; + } +} + +@each $animal in puma, sea-slug, cheerfulDog, BigSalamander, "string", 'another-string', "camelCaseString", "PascalCaseString" { + .#{$animal}-icon { + background-image: url('/images/#{$animal}.png'); + } +} + +$i: 6; +@while $i > 0 { + .item-#{$i} { + width: 2em * $i; + } + $i: $i - 2; +} + +@mixin cool-border($width: 10px, $coolStyle: 'solid', $AwesomeColor: "black") { + border: $width $coolStyle $AwesomeColor; +} + +p { + @include cool-border(1px, "solid", $fff); +} +p { + @include cool-border($width: 1px, $coolStyle: 'solid', $AwesomeColor: #fff); +} +p { + @include coolBorder(); +} + +@mixin coolBorder() { + border: 10px solid #fff; +} +p { + @include coolBorder(1px, "solid", $fff); +} + +@mixin CoolBorder() { + border: 10px solid #fff; +} +p { + @include CoolBorder(1px, "solid", $fff); +} + +@mixin box-shadow($shadows...) { + -moz-box-shadow: $shadows; + -webkit-box-shadow: $shadows; + box-shadow: $shadows; +} +.shadows { + @include box-shadow(0px 4px 5px #666, 2px 6px 10px #999); +} + +@mixin apply-to-ie6-only { + * html { + @content; + } +} +@include apply-to-ie6-only { + #logo { + background-image: url(/logo.gif); + } +} + +@mixin applyToIe6Only { + * html { + @content; + } +} +@include applyToIe6Only { + #logo { + background-image: url(/logo.gif); + } +} + +@mixin ApplyToIe6Only { + * html { + @content; + } +} +@include ApplyToIe6Only { + #logo { + background-image: url(/logo.gif); + } +} + +@mixin config-icon-colors($prefix, $colors...) { + @each $i in $colors { + .#{$prefix}#{nth($i, 1)} { + color: nth($i, 2); + } + } +} +@include config-icon-colors( + "icon-", + "save" green, + "cancel" gray, + "delete" red, + 'wait' blue +); + +@function my-calculation-function($some-number, $anotherNumber, $BigNumber){ + @return $some-number + $anotherNumber + $BigNumber; +} +@function myCalculationFunction($some-number, $anotherNumber, $BigNumber){ + @return $some-number + $anotherNumber + $BigNumber; +} +@function AnotherMyCalculationFunction($some-number, $anotherNumber, $BigNumber: 100px){ + @return $some-number + $anotherNumber + $BigNumber; +} +@function border($borders...) { + @return $borders; +} +.foo { + padding: my-calculation-function(10px, 5px, 100px); + margin: myCalculationFunction($some-number: 10px, $anotherNumber: 5px, $BigNumber: 100px); + width: AnotherMyCalculationFunction(10px, 5px); + border: border(25px, 35px); +} + +$sm-only: '(min-width: 768px) and (max-width: 991px)'; +$lg-and-up: '(min-width: 1200px)'; + +@media screen and #{$sm-only, $lg-and-up} { + color: #000; +} + +.class-#{$var} { + #{$var}: #7b3d66; + #{$attr}-color: blue; + #{$prop}-#{$side}: $value; + background-#{$var}: #7b3d66; + animation-name: #{var}; + line-height: #{strip-unit($line-height)}em; + height: 1#{$var}; + width: calc(100% - #{$sidebar-width}); + max-width: calc(#{$m*100}vw #{$sign} #{$b}); + font: #{$font-size}/#{$line-height}; + content: "I have #{8 + 2} books on SASS!"; + border: #{$var} #{$var} #{$var}; + filter: #{$var}#{$var}#{$var}; + prop: #{ $var + $var } #{ $var + $var } #{ $var + $var }; + prop2: + #{ + $var + + + $var + } + + #{ + $var + + + $var + } + + #{ + $var + + + $var + } + ; + prop3: + + #{ + + $var + + + + + $var + + } + + #{ + + $var + + + + + $var + + } + + #{ + + $var + + + + + $var + + } + ; + prop4: -#{$loader-icon-duration}; + prop5: +#{$loader-icon-duration}; + prop6: calc(-#{$loader-icon-duration} + 10); + prop7: calc(10 + -#{$loader-icon-duration}); +} + +/* Framework version for the generated CSS is #{$version}. */ + +.selector { + foo: bar; + #{$active} { + baz: qux; + } +} + +.el:nth-of-type(#{$i}) {} + +@media #{$value} {} + +@import url(#{$foundation-dir}/foundation/components/grid); + +@keyframes loader { + 0% { + transform: translate3d(0, 0, 0); + } + + #{50% - $loader-icon-duration} { + transform: translate3d(0, $bounce-height, 0); + } + + 50% { + transform: translate3d(0, $bounce-height, 0) scale($loader-bounce-horizontal-expansion, $loader-bounce-vertical-compression); + } +} + +$icons: wifi "\600", wifi-hotspot "\601", weather "\602"; + +@each $icon in $icons { + .icon-#{nth($icon, 1)}, %icon-#{nth($icon, 1)} { + content: "#{nth($icon, 2)}"; + } +} + +.foo { + prop: -($grid-gutter-width / 2); + prop1: -( $grid-gutter-width / 2 ); + prop2: -$grid-gutter-width / 2; + prop3: +($grid-gutter-width / 2); + prop4: 10px/8px; /* Plain CSS, no division */ + prop5: $width / 2; /* Uses a variable, does division */ + prop6: round(1.5) / 2; /* Uses a function, does division */ + prop7: (500px / 2); /* Uses parentheses, does division */ + prop8: 5px + 8px / 2px; //* Uses +, does division */ + prop9: (italic bold 10px/8px); /* In a list, parentheses don't count */ + prop10: #010203 + #040506; + prop11: #010203 * 2; + prop12: rgba(255, 0, 0, 0.75) + rgba(0, 255, 0, 0.75); + prop13: progid:DXImageTransform.Microsoft.gradient(enabled='false', startColorstr='#{ie-hex-str($green)}', endColorstr='#{ie-hex-str($translucent-red)}'); + prop14: e + -resize; + prop15: sans- + "serif"; + prop16: 1em + (2em * 3); + prop17: rotate(-2deg); + prop18: rotate( -2deg ) ; + _:_; + prop19: 10 - ($grid-gutter-width / 2); + prop20: 10 + -($grid-gutter-width / 2); + prop21: 10 + - ( $grid-gutter-width / 2 ) ; + prop22: - ( $grid-gutter-width / 2 ) ; + prop23: - ( $grid-gutter-width / 2 ) ; + prop24: -$grid-gutter-width; + prop25: + ( $grid-gutter-width / 2 ) ; + prop26: + ( $grid-gutter-width / 2 ) ; + prop27: +$grid-gutter-width; + prop28: --($grid-gutter-width / 2); + prop28: ++($grid-gutter-width / 2); + prop29: rotate( - 2deg ) ; +} + +$last:nth($juggler,length($juggler)); +$x:if($last%2==0,1/2,3/2); +$new:pow($last,$x); +$sequence:1,1 1,2 1,1 2 1 1, 1 1 1 2 2 1; +$new-entry:(); +$new-entry : ( ) ; +$new-entry : ( ) ; +$new-entry +: +( +) +; + +body:before { + content: quote(to-string(fibonacci(100), ' \A ')); + white-space: pre-wrap; +} + +width: ((100% - (($numPerRow - 1) * $margin)) / $numPerRow); +width +: +( +( +100% +- +( +( +$numPerRow +- +1 +) +* +$margin +) +) +/ +$numPerRow +) +; + +a:nth-child(#{$numPerRow}n) { + margin-right: 0; + margin-bottom: 0; +} + +@function em($pixels, $context: $browser-context) { + @return #{ $pixels / $context }em +} + +.navigation { + @extend %updated-#{$flag}; + @extend .selected-#{$flag}; + @extend %#{$item}; +} + +.icon-#{$icon-name} { + background-image: '/images/#{$icon-name}.svg'; +} + +$extmods:(eot:"?",svg:"#" + str-replace($name," ","_")); + +@mixin keyframes {@-moz-keyframes{@content;}@-webkit-keyframes{@content;}} + +@function gcd($a,$b){ + // From: http://rosettacode.org/wiki/Greatest_common_divisor#JavaScript + @if ($b != 0) { + @return gcd($b,$a % $b); + }@else{ + @return abs($a); + } +} + +$colors: ( +primary: ( +base: #00abc9, +light: #daf1f6, +dark: #12799a +), +secondary: ( +base: #424d55, +light: #ccc, +lightest: #efefef, +dark: #404247 +), +success: ( +base: #bbd33e, +light: #eaf0c6 +) +); + +@function color($color, $tone: "base") { +@return map-get(map-get($colors, $color), $tone); +} diff --git a/tests/css_selector_list/__snapshots__/jsfmt.spec.js.snap b/tests/css_selector_list/__snapshots__/jsfmt.spec.js.snap index f71441bd..7a4ccb86 100644 --- a/tests/css_selector_list/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_selector_list/__snapshots__/jsfmt.spec.js.snap @@ -9,7 +9,310 @@ exports[`selectors.css 1`] = ` } .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas, -.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas {}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas {} +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas, +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas +{} +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas, + +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + +{} + +.parent,.another-parent{} +.parent ,.another-parent{} +.parent, .another-parent{} +.parent , .another-parent {} +.parent , .another-parent {} +.parent +, +.another-parent +{} + +.parent.child {} +.parent .child {} +.parent .child {} +.parent { + .child-one {} + .child-two {} +} +.parent{ +.child-one {} +.child-two {} +} +.parent { + .child-one {} + .child-two {} +} +.parent { + .child-one {} + .child-two {} +} +.parent +{ + .child-one + {} + .child-two + {} +} +.parent + +{ + .child-one + + {} + .child-two + + {} +} + +.parent>.child {} +.parent > .child {} +.parent > .child {} +.parent>{ + .child-one {} + .child-two {} +} +.parent > { + .child-one {} + .child-two {} +} +.parent > { + .child-one {} + .child-two {} +} +.parent +> +{ + .child-one + {} + .child-two + {} +} +.parent + +> + +{ + .child-one + + {} + .child-two + + {} +} +.parent { +>.child-one {} +>.child-two {} +} +.parent { + > .child-one {} + > .child-two {} +} +.parent { + > .child-one {} + > .child-two {} +} +.parent { + > .child-one {} + > .child-two {} +} +.parent { + > .child-one {} + > .child-two {} +} + +.parent+.child {} +.parent + .child {} +.parent + .child {} +.parent+{ + .child-one {} + .child-two {} +} +.parent + { + .child-one {} + .child-two {} +} +.parent + { + .child-one {} + .child-two {} +} +.parent ++ +{ + .child-one + {} + .child-two + {} +} +.parent + ++ + +{ + .child-one + + {} + .child-two + + {} +} +.parent { ++.child-one {} ++.child-two {} +} +.parent { + + .child-one {} + + .child-two {} +} +.parent { + + .child-one {} + + .child-two {} +} +.parent { + + .child-one {} + + .child-two {} +} +.parent { + + .child-one {} + + .child-two {} +} + +.parent~.child {} +.parent ~ .child {} +.parent ~ .child {} +.parent~{ + .child-one {} + .child-two {} +} +.parent ~ { + .child-one {} + .child-two {} +} +.parent ~ { + .child-one {} + .child-two {} +} +.parent +~ +{ + .child-one + {} + .child-two + {} +} +.parent + +~ + +{ + .child-one + + {} + .child-two + + {} +} +.parent { +~.child-one {} +~.child-two {} +} +.parent { + ~ .child-one {} + ~ .child-two {} +} +.parent { + ~ .child-one {} + ~ .child-two {} +} +.parent { + ~ .child-one {} + ~ .child-two {} +} +.parent { + ~ .child-one {} + ~ .child-two {} +} + +.parent>>>.child {} +.parent >>> .child {} +.parent >>> .child {} +.parent>>>{ + .child-one {} + .child-two {} +} +.parent >>> { + .child-one {} + .child-two {} +} +.parent >>> { + .child-one {} + .child-two {} +} +.parent +>>> +{ + .child-one + {} + .child-two + {} +} +.parent + +>>> + +{ + .child-one + + {} + .child-two + + {} +} +.parent { +>>>.child-one {} +>>>.child-two {} +} +.parent { + >>> .child-one {} + >>> .child-two {} +} +.parent { + >>> .child-one {} + >>> .child-two {} +} +.parent { + >>> .child-one {} + >>> .child-two {} +} +.parent { + >>> .child-one {} + >>> .child-two {} +} + +/* This matches all XHTML elements, as XHTML is the default unprefixed namespace */ +a {} + +/* This matches all SVG elements */ +svg|a {} + +/* This matches both XHTML and SVG elements */ +*|a {} + +|B {} + +ns|* {} + +*|* {} + +|* {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .a, .b, .c { @@ -33,5 +336,369 @@ exports[`selectors.css 1`] = ` .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas { } +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas, +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas { +} +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas, +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas { +} + +.parent, +.another-parent { +} +.parent, +.another-parent { +} +.parent, +.another-parent { +} +.parent, +.another-parent { +} +.parent, +.another-parent { +} +.parent, +.another-parent { +} + +.parent.child { +} +.parent .child { +} +.parent .child { +} +.parent { + .child-one { + } + .child-two { + } +} +.parent { + .child-one { + } + .child-two { + } +} +.parent { + .child-one { + } + .child-two { + } +} +.parent { + .child-one { + } + .child-two { + } +} +.parent { + .child-one { + } + .child-two { + } +} +.parent { + .child-one { + } + .child-two { + } +} + +.parent > .child { +} +.parent > .child { +} +.parent > .child { +} +.parent > { + .child-one { + } + .child-two { + } +} +.parent > { + .child-one { + } + .child-two { + } +} +.parent > { + .child-one { + } + .child-two { + } +} +.parent > { + .child-one { + } + .child-two { + } +} +.parent > { + .child-one { + } + .child-two { + } +} +.parent { + > .child-one { + } + > .child-two { + } +} +.parent { + > .child-one { + } + > .child-two { + } +} +.parent { + > .child-one { + } + > .child-two { + } +} +.parent { + > .child-one { + } + > .child-two { + } +} +.parent { + > .child-one { + } + > .child-two { + } +} + +.parent + .child { +} +.parent + .child { +} +.parent + .child { +} +.parent + { + .child-one { + } + .child-two { + } +} +.parent + { + .child-one { + } + .child-two { + } +} +.parent + { + .child-one { + } + .child-two { + } +} +.parent + { + .child-one { + } + .child-two { + } +} +.parent + { + .child-one { + } + .child-two { + } +} +.parent { + + .child-one { + } + + .child-two { + } +} +.parent { + + .child-one { + } + + .child-two { + } +} +.parent { + + .child-one { + } + + .child-two { + } +} +.parent { + + .child-one { + } + + .child-two { + } +} +.parent { + + .child-one { + } + + .child-two { + } +} + +.parent ~ .child { +} +.parent ~ .child { +} +.parent ~ .child { +} +.parent ~ { + .child-one { + } + .child-two { + } +} +.parent ~ { + .child-one { + } + .child-two { + } +} +.parent ~ { + .child-one { + } + .child-two { + } +} +.parent ~ { + .child-one { + } + .child-two { + } +} +.parent ~ { + .child-one { + } + .child-two { + } +} +.parent { + ~ .child-one { + } + ~ .child-two { + } +} +.parent { + ~ .child-one { + } + ~ .child-two { + } +} +.parent { + ~ .child-one { + } + ~ .child-two { + } +} +.parent { + ~ .child-one { + } + ~ .child-two { + } +} +.parent { + ~ .child-one { + } + ~ .child-two { + } +} + +.parent >>> .child { +} +.parent >>> .child { +} +.parent >>> .child { +} +.parent >>> { + .child-one { + } + .child-two { + } +} +.parent >>> { + .child-one { + } + .child-two { + } +} +.parent >>> { + .child-one { + } + .child-two { + } +} +.parent >>> { + .child-one { + } + .child-two { + } +} +.parent >>> { + .child-one { + } + .child-two { + } +} +.parent { + >>> .child-one { + } + >>> .child-two { + } +} +.parent { + >>> .child-one { + } + >>> .child-two { + } +} +.parent { + >>> .child-one { + } + >>> .child-two { + } +} +.parent { + >>> .child-one { + } + >>> .child-two { + } +} +.parent { + >>> .child-one { + } + >>> .child-two { + } +} + +/* This matches all XHTML elements, as XHTML is the default unprefixed namespace */ +a { +} + +/* This matches all SVG elements */ +svg|a { +} + +/* This matches both XHTML and SVG elements */ +*|a { +} + +|b { +} + +ns|* { +} + +*|* { +} + +|* { +} `; diff --git a/tests/css_selector_list/selectors.css b/tests/css_selector_list/selectors.css index 2a8c2ca7..26eedf45 100644 --- a/tests/css_selector_list/selectors.css +++ b/tests/css_selector_list/selectors.css @@ -6,4 +6,306 @@ } .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas, -.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas {} \ No newline at end of file +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas .asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas {} +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas, +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas +{} +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas, + +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + +.asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkas + +{} + +.parent,.another-parent{} +.parent ,.another-parent{} +.parent, .another-parent{} +.parent , .another-parent {} +.parent , .another-parent {} +.parent +, +.another-parent +{} + +.parent.child {} +.parent .child {} +.parent .child {} +.parent { + .child-one {} + .child-two {} +} +.parent{ +.child-one {} +.child-two {} +} +.parent { + .child-one {} + .child-two {} +} +.parent { + .child-one {} + .child-two {} +} +.parent +{ + .child-one + {} + .child-two + {} +} +.parent + +{ + .child-one + + {} + .child-two + + {} +} + +.parent>.child {} +.parent > .child {} +.parent > .child {} +.parent>{ + .child-one {} + .child-two {} +} +.parent > { + .child-one {} + .child-two {} +} +.parent > { + .child-one {} + .child-two {} +} +.parent +> +{ + .child-one + {} + .child-two + {} +} +.parent + +> + +{ + .child-one + + {} + .child-two + + {} +} +.parent { +>.child-one {} +>.child-two {} +} +.parent { + > .child-one {} + > .child-two {} +} +.parent { + > .child-one {} + > .child-two {} +} +.parent { + > .child-one {} + > .child-two {} +} +.parent { + > .child-one {} + > .child-two {} +} + +.parent+.child {} +.parent + .child {} +.parent + .child {} +.parent+{ + .child-one {} + .child-two {} +} +.parent + { + .child-one {} + .child-two {} +} +.parent + { + .child-one {} + .child-two {} +} +.parent ++ +{ + .child-one + {} + .child-two + {} +} +.parent + ++ + +{ + .child-one + + {} + .child-two + + {} +} +.parent { ++.child-one {} ++.child-two {} +} +.parent { + + .child-one {} + + .child-two {} +} +.parent { + + .child-one {} + + .child-two {} +} +.parent { + + .child-one {} + + .child-two {} +} +.parent { + + .child-one {} + + .child-two {} +} + +.parent~.child {} +.parent ~ .child {} +.parent ~ .child {} +.parent~{ + .child-one {} + .child-two {} +} +.parent ~ { + .child-one {} + .child-two {} +} +.parent ~ { + .child-one {} + .child-two {} +} +.parent +~ +{ + .child-one + {} + .child-two + {} +} +.parent + +~ + +{ + .child-one + + {} + .child-two + + {} +} +.parent { +~.child-one {} +~.child-two {} +} +.parent { + ~ .child-one {} + ~ .child-two {} +} +.parent { + ~ .child-one {} + ~ .child-two {} +} +.parent { + ~ .child-one {} + ~ .child-two {} +} +.parent { + ~ .child-one {} + ~ .child-two {} +} + +.parent>>>.child {} +.parent >>> .child {} +.parent >>> .child {} +.parent>>>{ + .child-one {} + .child-two {} +} +.parent >>> { + .child-one {} + .child-two {} +} +.parent >>> { + .child-one {} + .child-two {} +} +.parent +>>> +{ + .child-one + {} + .child-two + {} +} +.parent + +>>> + +{ + .child-one + + {} + .child-two + + {} +} +.parent { +>>>.child-one {} +>>>.child-two {} +} +.parent { + >>> .child-one {} + >>> .child-two {} +} +.parent { + >>> .child-one {} + >>> .child-two {} +} +.parent { + >>> .child-one {} + >>> .child-two {} +} +.parent { + >>> .child-one {} + >>> .child-two {} +} + +/* This matches all XHTML elements, as XHTML is the default unprefixed namespace */ +a {} + +/* This matches all SVG elements */ +svg|a {} + +/* This matches both XHTML and SVG elements */ +*|a {} + +|B {} + +ns|* {} + +*|* {} + +|* {} diff --git a/tests/css_trailing_comma/__snapshots__/jsfmt.spec.js.snap b/tests/css_trailing_comma/__snapshots__/jsfmt.spec.js.snap index 443fca15..b9a310af 100644 --- a/tests/css_trailing_comma/__snapshots__/jsfmt.spec.js.snap +++ b/tests/css_trailing_comma/__snapshots__/jsfmt.spec.js.snap @@ -1,13 +1,86 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`trailing.css 1`] = ` +exports[`declaration.css 1`] = ` +a { + margin: $bar,; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +a { + margin: $bar; +} + +`; + +exports[`list.css 1`] = ` +$colors: hotpink deepskyblue firebrick,; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +$colors: hotpink deepskyblue firebrick; + +`; + +exports[`map.css 1`] = ` +$map: (a,); +$map: ('Helvetica', 'Arial', sans-serif,); +$map: ( + small: 767px, + medium: 992px, + large: 1200px, +); +$map: ( + 'medium': (min-width: 800px), + 'large': (min-width: 1000px), + 'huge': (min-width: 1200px), +); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +$map: (a); +$map: ( + "Helvetica", + "Arial", + sans-serif +); +$map: ( + small: 767px, + medium: 992px, + large: 1200px +); +$map: ( + "medium": (min-width: 800px), + "large": (min-width: 1000px), + "huge": (min-width: 1200px) +); + +`; + +exports[`selector_list.css 1`] = ` asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkasdhkajsdhakjsdhkajsdhjkahskjdkjahsjkdjkakjsdm, asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkasdhkajsdhakjsdhkajsdhjkahskjdkjahsjkdjkakjsdm, { } + +.some-class, { +&.another-class, { + color: red; + } +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkasdhkajsdhakjsdhkajsdhjkahskjdkjahsjkdjkakjsdm, asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkasdhkajsdhakjsdhkajsdhjkahskjdkjahsjkdjkakjsdm { } +.some-class { + &.another-class { + color: red; + } +} + +`; + +exports[`variable.css 1`] = ` +$test: 1,; +$margin: 0, 2em, 0, 1.5em,; + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +$test: 1; +$margin: 0, 2em, 0, 1.5em; + `; diff --git a/tests/css_trailing_comma/declaration.css b/tests/css_trailing_comma/declaration.css new file mode 100644 index 00000000..0241a308 --- /dev/null +++ b/tests/css_trailing_comma/declaration.css @@ -0,0 +1,3 @@ +a { + margin: $bar,; +} diff --git a/tests/css_trailing_comma/list.css b/tests/css_trailing_comma/list.css new file mode 100644 index 00000000..d8d5aa5a --- /dev/null +++ b/tests/css_trailing_comma/list.css @@ -0,0 +1 @@ +$colors: hotpink deepskyblue firebrick,; diff --git a/tests/css_trailing_comma/map.css b/tests/css_trailing_comma/map.css new file mode 100644 index 00000000..ca883a7a --- /dev/null +++ b/tests/css_trailing_comma/map.css @@ -0,0 +1,12 @@ +$map: (a,); +$map: ('Helvetica', 'Arial', sans-serif,); +$map: ( + small: 767px, + medium: 992px, + large: 1200px, +); +$map: ( + 'medium': (min-width: 800px), + 'large': (min-width: 1000px), + 'huge': (min-width: 1200px), +); diff --git a/tests/css_trailing_comma/trailing.css b/tests/css_trailing_comma/selector_list.css similarity index 76% rename from tests/css_trailing_comma/trailing.css rename to tests/css_trailing_comma/selector_list.css index 1e4d9ea8..8b9f8952 100644 --- a/tests/css_trailing_comma/trailing.css +++ b/tests/css_trailing_comma/selector_list.css @@ -2,3 +2,9 @@ asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkasdhkajsdhakjsdhkajsdhjkahskjdkjahsj asdasldaskdhjkashdahsdkjahskdjhakjsdkjahsdhkasdhkajsdhakjsdhkajsdhjkahskjdkjahsjkdjkakjsdm, { } + +.some-class, { +&.another-class, { + color: red; + } +} diff --git a/tests/css_trailing_comma/variable.css b/tests/css_trailing_comma/variable.css new file mode 100644 index 00000000..648ca1f7 --- /dev/null +++ b/tests/css_trailing_comma/variable.css @@ -0,0 +1,3 @@ +$test: 1,; +$margin: 0, 2em, 0, 1.5em,; + diff --git a/tests/css_variables/__snapshots__/jsfmt.spec.js.snap b/tests/css_variables/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..92b54730 --- /dev/null +++ b/tests/css_variables/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,30 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`variables.css 1`] = ` +.foo { + --prop: 10px; + prop1: var(--prop); + prop2: var(--my-var, --my-background, pink); + prop3: calc(var(--prop) * 1px); +} + +@supports (--foo: green) { + body { + color: var(--varName); + } +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +.foo { + --prop: 10px; + prop1: var(--prop); + prop2: var(--my-var, --my-background, pink); + prop3: calc(var(--prop) * 1px); +} + +@supports (--foo: green) { + body { + color: var(--varName); + } +} + +`; diff --git a/tests/css_variables/jsfmt.spec.js b/tests/css_variables/jsfmt.spec.js new file mode 100644 index 00000000..7d3726c8 --- /dev/null +++ b/tests/css_variables/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["css"]); diff --git a/tests/css_variables/variables.css b/tests/css_variables/variables.css new file mode 100644 index 00000000..ff6556f4 --- /dev/null +++ b/tests/css_variables/variables.css @@ -0,0 +1,12 @@ +.foo { + --prop: 10px; + prop1: var(--prop); + prop2: var(--my-var, --my-background, pink); + prop3: calc(var(--prop) * 1px); +} + +@supports (--foo: green) { + body { + color: var(--varName); + } +} diff --git a/tests/css_yaml/__snapshots__/jsfmt.spec.js.snap b/tests/css_yaml/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..02918d1a --- /dev/null +++ b/tests/css_yaml/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,64 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`yaml.css 1`] = ` +--- +title: Title +description: Description +--- + +a { + color: red; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +title: Title +description: Description +--- + +a { + color: red; +} + +`; + +exports[`yaml.less 1`] = ` +--- +title: Title +description: Description +--- + +a { + color: red; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +title: Title +description: Description +--- + +a { + color: red; +} + +`; + +exports[`yaml.scss 1`] = ` +--- +title: Title +description: Description +--- + +a { + color: red; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +title: Title +description: Description +--- + +a { + color: red; +} + +`; diff --git a/tests/css_yaml/jsfmt.spec.js b/tests/css_yaml/jsfmt.spec.js new file mode 100644 index 00000000..7d3726c8 --- /dev/null +++ b/tests/css_yaml/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["css"]); diff --git a/tests/css_yaml/yaml.css b/tests/css_yaml/yaml.css new file mode 100644 index 00000000..c4d779af --- /dev/null +++ b/tests/css_yaml/yaml.css @@ -0,0 +1,8 @@ +--- +title: Title +description: Description +--- + +a { + color: red; +} diff --git a/tests/css_yaml/yaml.less b/tests/css_yaml/yaml.less new file mode 100644 index 00000000..c6360667 --- /dev/null +++ b/tests/css_yaml/yaml.less @@ -0,0 +1,8 @@ +--- +title: Title +description: Description +--- + +a { + color: red; +} diff --git a/tests/css_yaml/yaml.scss b/tests/css_yaml/yaml.scss new file mode 100644 index 00000000..c6360667 --- /dev/null +++ b/tests/css_yaml/yaml.scss @@ -0,0 +1,8 @@ +--- +title: Title +description: Description +--- + +a { + color: red; +} diff --git a/tests/destructuring/__snapshots__/jsfmt.spec.js.snap b/tests/destructuring/__snapshots__/jsfmt.spec.js.snap index 6af37316..0c932673 100644 --- a/tests/destructuring/__snapshots__/jsfmt.spec.js.snap +++ b/tests/destructuring/__snapshots__/jsfmt.spec.js.snap @@ -4,9 +4,56 @@ exports[`destructuring.js 1`] = ` const [one, two = null, three = null] = arr; a = ([s=1,]) => 1 const { children, ...props } = this.props + +const { user: { firstName, lastName } } = this.props; + +const { + name: { first, last }, + organisation: { address: { street: orgStreetAddress, postcode: orgPostcode } } +} = user; + +function f({ data: { name } }) {} + +const UserComponent = function({ + name: { first, last }, + organisation: { address: { street: orgStreetAddress, postcode: orgPostcode } }, +}) { + return +}; + +const { a, b, c, d: { e } } = someObject; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ const [one, two = null, three = null] = arr; a = ([s = 1]) => 1; const { children, ...props } = this.props; +const { + user: { firstName, lastName } +} = this.props; + +const { + name: { first, last }, + organisation: { + address: { street: orgStreetAddress, postcode: orgPostcode } + } +} = user; + +function f({ data: { name } }) {} + +const UserComponent = function({ + name: { first, last }, + organisation: { + address: { street: orgStreetAddress, postcode: orgPostcode } + } +}) { + return; +}; + +const { + a, + b, + c, + d: { e } +} = someObject; + `; diff --git a/tests/destructuring/destructuring.js b/tests/destructuring/destructuring.js index c26994b1..6b774e3d 100644 --- a/tests/destructuring/destructuring.js +++ b/tests/destructuring/destructuring.js @@ -1,3 +1,21 @@ const [one, two = null, three = null] = arr; a = ([s=1,]) => 1 const { children, ...props } = this.props + +const { user: { firstName, lastName } } = this.props; + +const { + name: { first, last }, + organisation: { address: { street: orgStreetAddress, postcode: orgPostcode } } +} = user; + +function f({ data: { name } }) {} + +const UserComponent = function({ + name: { first, last }, + organisation: { address: { street: orgStreetAddress, postcode: orgPostcode } }, +}) { + return +}; + +const { a, b, c, d: { e } } = someObject; diff --git a/tests/empty/__snapshots__/jsfmt.spec.js.snap b/tests/empty/__snapshots__/jsfmt.spec.js.snap index 874fa35e..9bfbb43e 100644 --- a/tests/empty/__snapshots__/jsfmt.spec.js.snap +++ b/tests/empty/__snapshots__/jsfmt.spec.js.snap @@ -6,3 +6,13 @@ exports[`empty.js 1`] = ` `; + +exports[`empty-file.js 1`] = ` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`; + +exports[`single-space.js 1`] = ` + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +`; diff --git a/tests/empty/empty-file.js b/tests/empty/empty-file.js new file mode 100644 index 00000000..e69de29b diff --git a/tests/empty/single-space.js b/tests/empty/single-space.js new file mode 100644 index 00000000..0519ecba --- /dev/null +++ b/tests/empty/single-space.js @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/tests/flow/constructor_annots/__snapshots__/jsfmt.spec.js.snap b/tests/flow/constructor_annots/__snapshots__/jsfmt.spec.js.snap index c58cdd8c..36b2fb6c 100644 --- a/tests/flow/constructor_annots/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/constructor_annots/__snapshots__/jsfmt.spec.js.snap @@ -19,7 +19,6 @@ interface IFooPrototype { interface IFoo extends IFooPrototype { x: boolean; // error, should have declared x: number instead static (): void; - static y: boolean; // error, should have declared static y: number instead constructor(): void; } exports.Foo2 = (Foo: Class); @@ -45,8 +44,7 @@ interface IFooPrototype { } interface IFoo extends IFooPrototype { x: boolean; // error, should have declared x: number instead - static (): void; - static y: boolean; // error, should have declared static y: number instead + static(): void; constructor(): void; } exports.Foo2 = (Foo: Class); diff --git a/tests/flow/constructor_annots/constructors.js b/tests/flow/constructor_annots/constructors.js index d7bfe938..219fe673 100644 --- a/tests/flow/constructor_annots/constructors.js +++ b/tests/flow/constructor_annots/constructors.js @@ -16,7 +16,6 @@ interface IFooPrototype { interface IFoo extends IFooPrototype { x: boolean; // error, should have declared x: number instead static (): void; - static y: boolean; // error, should have declared static y: number instead constructor(): void; } exports.Foo2 = (Foo: Class); diff --git a/tests/flow/declare_export/__snapshots__/jsfmt.spec.js.snap b/tests/flow/declare_export/__snapshots__/jsfmt.spec.js.snap index 6e430513..7de36595 100644 --- a/tests/flow/declare_export/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/declare_export/__snapshots__/jsfmt.spec.js.snap @@ -142,7 +142,7 @@ declare export default () => number; * @flow */ -declare export default () => number +declare export default () => number; `; @@ -159,7 +159,7 @@ declare export default () =>number; * @flow */ -declare export default () => number +declare export default () => number; `; @@ -251,7 +251,7 @@ declare export var str: string; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /* @flow */ -declare export default number +declare export default number; declare export var str: string; `; diff --git a/tests/flow/refinements/__snapshots__/jsfmt.spec.js.snap b/tests/flow/refinements/__snapshots__/jsfmt.spec.js.snap index fa559cdd..8bdcaaf9 100644 --- a/tests/flow/refinements/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/refinements/__snapshots__/jsfmt.spec.js.snap @@ -2571,7 +2571,8 @@ type Breakfast = Apple | Orange | Broccoli | Carrot; function bar(x: Breakfast) { if (x.kind === "Fruit") { (x.taste: "Good"); - } else (x.raw: "No"); // error, Apple.taste = Bad // error, Carrot.raw = Maybe + } // error, Apple.taste = Bad + else (x.raw: "No"); // error, Carrot.raw = Maybe } function qux(x: Breakfast) { diff --git a/tests/flow/union/__snapshots__/jsfmt.spec.js.snap b/tests/flow/union/__snapshots__/jsfmt.spec.js.snap index 51587ffb..056241c2 100644 --- a/tests/flow/union/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow/union/__snapshots__/jsfmt.spec.js.snap @@ -29,19 +29,19 @@ export class PlusOp extends BinaryExpression { +export class MinusOp extends BinaryExpression { } -export class MulOp extends BinaryExpression { +export class MulOp extends BinaryExpression { } -export class DivOp extends BinaryExpression { +export class DivOp extends BinaryExpression { } -export class ModOp extends BinaryExpression { +export class ModOp extends BinaryExpression { } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -77,21 +77,29 @@ export class PlusOp extends BinaryExpression< > {} export class MinusOp extends BinaryExpression< + ArithmeticExpression, + ArithmeticExpression, ArithmeticExpression, ArithmeticExpression > {} export class MulOp extends BinaryExpression< + ArithmeticExpression, + ArithmeticExpression, ArithmeticExpression, ArithmeticExpression > {} export class DivOp extends BinaryExpression< + ArithmeticExpression, + ArithmeticExpression, ArithmeticExpression, ArithmeticExpression > {} export class ModOp extends BinaryExpression< + ArithmeticExpression, + ArithmeticExpression, ArithmeticExpression, ArithmeticExpression > {} diff --git a/tests/flow/union/blowup.js b/tests/flow/union/blowup.js index a480219d..96fdd9cb 100644 --- a/tests/flow/union/blowup.js +++ b/tests/flow/union/blowup.js @@ -26,18 +26,18 @@ export class PlusOp extends BinaryExpression { +export class MinusOp extends BinaryExpression { } -export class MulOp extends BinaryExpression { +export class MulOp extends BinaryExpression { } -export class DivOp extends BinaryExpression { +export class DivOp extends BinaryExpression { } -export class ModOp extends BinaryExpression { +export class ModOp extends BinaryExpression { } diff --git a/tests/flow_function_parentheses/__snapshots__/jsfmt.spec.js.snap b/tests/flow_function_parentheses/__snapshots__/jsfmt.spec.js.snap index f193c2ff..995d58e3 100644 --- a/tests/flow_function_parentheses/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow_function_parentheses/__snapshots__/jsfmt.spec.js.snap @@ -34,6 +34,23 @@ const selectorByPath: Path => SomethingSelector< `; +exports[`single.js 3`] = ` +const selectorByPath: + Path + => SomethingSelector< + SomethingUEditorContextType, + SomethingUEditorContextType, + SomethingBulkValue +> = memoizeWithArgs(/* ... */) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const selectorByPath: (Path) => SomethingSelector< + SomethingUEditorContextType, + SomethingUEditorContextType, + SomethingBulkValue +> = memoizeWithArgs(/* ... */); + +`; + exports[`test.js 1`] = ` type Banana = { eat: string => boolean, @@ -219,3 +236,96 @@ interface F { type ExtractType = (B) => D; `; + +exports[`test.js 3`] = ` +type Banana = { + eat: string => boolean, +}; + +type Hex = {n: 0x01}; + +type T = { method: (a) => void }; + +type T = { method(a): void }; + +declare class X { method(a): void } + +declare function f(a): void; + +var f: (a) => void; + +interface F { m(string): number } + +interface F { m: (string) => number } + +function f(o: { f: (string) => void }) {} + +function f(o: { f(string): void }) {} + +type f = (...arg) => void; + +type f = (/* comment */ arg) => void; + +type f = (arg /* comment */) => void; + +type f = (?arg) => void; + +class X { + constructor( + ideConnectionFactory: child_process$ChildProcess => FlowIDEConnection = + defaultIDEConnectionFactory, + ) { + } +} + +interface F { + ideConnectionFactoryLongLongLong: (child_process$ChildProcess) => FlowIDEConnection +} + +type ExtractType = (B) => D +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type Banana = { + eat: (string) => boolean +}; + +type Hex = { n: 0x01 }; + +type T = { method: (a) => void }; + +type T = { method(a): void }; + +declare class X { method(a): void } + +declare function f(a): void; + +var f: (a) => void; + +interface F { m(string): number } + +interface F { m: (string) => number } + +function f(o: { f: (string) => void }) {} + +function f(o: { f(string): void }) {} + +type f = (...arg) => void; + +type f = (/* comment */ arg) => void; + +type f = (arg /* comment */) => void; + +type f = (?arg) => void; + +class X { + constructor( + ideConnectionFactory: (child_process$ChildProcess) => FlowIDEConnection = defaultIDEConnectionFactory + ) {} +} + +interface F { + ideConnectionFactoryLongLongLong: (child_process$ChildProcess) => FlowIDEConnection; +} + +type ExtractType = (B) => D; + +`; diff --git a/tests/flow_function_parentheses/jsfmt.spec.js b/tests/flow_function_parentheses/jsfmt.spec.js index 425bb9ff..d0170fda 100644 --- a/tests/flow_function_parentheses/jsfmt.spec.js +++ b/tests/flow_function_parentheses/jsfmt.spec.js @@ -1,2 +1,3 @@ run_spec(__dirname, ["flow", "babylon"]); run_spec(__dirname, ["flow", "babylon"], { trailingComma: "all" }); +run_spec(__dirname, ["flow", "babylon"], { arrowParens: "always" }); diff --git a/tests/flow_maybe/__snapshots__/jsfmt.spec.js.snap b/tests/flow_maybe/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..9c699ea7 --- /dev/null +++ b/tests/flow_maybe/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,8 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`maybe_return.js 1`] = ` +function getScaledData({x}): ?{foo: number} {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +function getScaledData({ x }): ?{ foo: number } {} + +`; diff --git a/tests/flow_maybe/jsfmt.spec.js b/tests/flow_maybe/jsfmt.spec.js new file mode 100644 index 00000000..c1ba82f4 --- /dev/null +++ b/tests/flow_maybe/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["flow", "babylon"]); diff --git a/tests/flow_maybe/maybe_return.js b/tests/flow_maybe/maybe_return.js new file mode 100644 index 00000000..7484452f --- /dev/null +++ b/tests/flow_maybe/maybe_return.js @@ -0,0 +1 @@ +function getScaledData({x}): ?{foo: number} {} diff --git a/tests/flow_type_cast/__snapshots__/jsfmt.spec.js.snap b/tests/flow_type_cast/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..a413b447 --- /dev/null +++ b/tests/flow_type_cast/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,25 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`expression.js 1`] = ` +let x: string = (foo: string); + +// https://github.com/prettier/prettier/issues/3936 +const foo = ((1?2:3): number); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +let x: string = (foo: string); + +// https://github.com/prettier/prettier/issues/3936 +const foo = ((1 ? 2 : 3): number); + +`; + +exports[`statement.js 1`] = ` +foo: string; +bar: number; +(foo.bar: SomeType); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +foo: string; +bar: number; +(foo.bar: SomeType); + +`; diff --git a/tests/flow_type_cast/expression.js b/tests/flow_type_cast/expression.js new file mode 100644 index 00000000..50ecacd5 --- /dev/null +++ b/tests/flow_type_cast/expression.js @@ -0,0 +1,4 @@ +let x: string = (foo: string); + +// https://github.com/prettier/prettier/issues/3936 +const foo = ((1?2:3): number); diff --git a/tests/flow_type_cast/jsfmt.spec.js b/tests/flow_type_cast/jsfmt.spec.js new file mode 100644 index 00000000..c1ba82f4 --- /dev/null +++ b/tests/flow_type_cast/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["flow", "babylon"]); diff --git a/tests/flow_type_cast/statement.js b/tests/flow_type_cast/statement.js new file mode 100644 index 00000000..0dc735b4 --- /dev/null +++ b/tests/flow_type_cast/statement.js @@ -0,0 +1,3 @@ +foo: string; +bar: number; +(foo.bar: SomeType); diff --git a/tests/flow_type_declarations/__snapshots__/jsfmt.spec.js.snap b/tests/flow_type_declarations/__snapshots__/jsfmt.spec.js.snap index d9349966..20c36519 100644 --- a/tests/flow_type_declarations/__snapshots__/jsfmt.spec.js.snap +++ b/tests/flow_type_declarations/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,19 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`declare_export.js 1`] = ` +declare export default 5; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare export default 5; + +`; + +exports[`declare_export.js 2`] = ` +declare export default 5; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare export default 5 + +`; + exports[`declare_type.js 1`] = ` declare type A = string; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -7,6 +21,13 @@ declare type A = string; `; +exports[`declare_type.js 2`] = ` +declare type A = string; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare type A = string + +`; + exports[`declare_var.js 1`] = ` declare var bool: React$PropType$Primitive; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -14,6 +35,13 @@ declare var bool: React$PropType$Primitive; `; +exports[`declare_var.js 2`] = ` +declare var bool: React$PropType$Primitive; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare var bool: React$PropType$Primitive + +`; + exports[`long.js 1`] = ` export type AdamPlacementValidationSingleErrorKey = 'SOME_FANCY_TARGETS.GLOBAL_TARGET'; @@ -35,6 +63,27 @@ type SomeOtherLongLongLongLongLongLongLongLongLongLongLongLongLongLongKey = Some `; +exports[`long.js 2`] = ` +export type AdamPlacementValidationSingleErrorKey = + 'SOME_FANCY_TARGETS.GLOBAL_TARGET'; + +export type SomeReallyLongLongLongLongLongLongLongLongLongLongLongLongLongLongKey = true; + +export type SomeOtherLongLongLongLongLongLongLongLongLongLongLongLongLongLongKey = null; + +type SomeOtherLongLongLongLongLongLongLongLongLongLongLongLongLongLongKey = SomeReallyLongLongLongLongLongLongIdentifier; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +export type AdamPlacementValidationSingleErrorKey = + "SOME_FANCY_TARGETS.GLOBAL_TARGET" + +export type SomeReallyLongLongLongLongLongLongLongLongLongLongLongLongLongLongKey = true + +export type SomeOtherLongLongLongLongLongLongLongLongLongLongLongLongLongLongKey = null + +type SomeOtherLongLongLongLongLongLongLongLongLongLongLongLongLongLongKey = SomeReallyLongLongLongLongLongLongIdentifier + +`; + exports[`opaque.js 1`] = ` declare export opaque type Foo; declare export opaque type Bar; @@ -67,3 +116,36 @@ opaque type union = { type: "A" } | { type: "B" }; opaque type overloads = ((x: string) => number) & ((x: number) => string); `; + +exports[`opaque.js 2`] = ` +declare export opaque type Foo; +declare export opaque type Bar; +declare export opaque type Baz: Foo; +declare export opaque type Foo: Bar; +declare export opaque type Foo: Bar; +declare export opaque type Foo: Bar; +opaque type ID = string; +opaque type Foo = Bar; +opaque type Maybe = _Maybe; +export opaque type Foo = number; +opaque type union = + | {type: "A"} + | {type: "B"}; +opaque type overloads = + & ((x: string) => number) + & ((x: number) => string); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +declare export opaque type Foo +declare export opaque type Bar +declare export opaque type Baz: Foo +declare export opaque type Foo: Bar +declare export opaque type Foo: Bar +declare export opaque type Foo: Bar +opaque type ID = string +opaque type Foo = Bar +opaque type Maybe = _Maybe +export opaque type Foo = number +opaque type union = { type: "A" } | { type: "B" } +opaque type overloads = ((x: string) => number) & ((x: number) => string) + +`; diff --git a/tests/flow_type_declarations/declare_export.js b/tests/flow_type_declarations/declare_export.js new file mode 100644 index 00000000..75b7fac8 --- /dev/null +++ b/tests/flow_type_declarations/declare_export.js @@ -0,0 +1 @@ +declare export default 5; diff --git a/tests/flow_type_declarations/jsfmt.spec.js b/tests/flow_type_declarations/jsfmt.spec.js index c1ba82f4..c5f8baf6 100644 --- a/tests/flow_type_declarations/jsfmt.spec.js +++ b/tests/flow_type_declarations/jsfmt.spec.js @@ -1 +1,2 @@ run_spec(__dirname, ["flow", "babylon"]); +run_spec(__dirname, ["flow", "babylon"], { semi: false }); diff --git a/tests/graphql_directive_decl/__snapshots__/jsfmt.spec.js.snap b/tests/graphql_directive_decl/__snapshots__/jsfmt.spec.js.snap index a9fb505d..5db608cf 100644 --- a/tests/graphql_directive_decl/__snapshots__/jsfmt.spec.js.snap +++ b/tests/graphql_directive_decl/__snapshots__/jsfmt.spec.js.snap @@ -1,23 +1,23 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`directive_decl.graphql 1`] = ` -directive @a on FIELD1 -directive @a(as: String) on FIELD1 -directive @a(as: String = 1) on FIELD1 -directive @a(as: String, b: Int!) on FIELD1 -directive @a(as: String! = 1 @deprecated) on FIELD1 -directive @a(as: String! = 1 @deprecated) on FIELD1 | FIELD2 +directive @a on QUERY +directive @a(as: String) on QUERY +directive @a(as: String = 1) on QUERY +directive @a(as: String, b: Int!) on QUERY +directive @a(as: String! = 1 @deprecated) on QUERY +directive @a(as: String! = 1 @deprecated) on QUERY | MUTATION ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -directive @a on FIELD1 +directive @a on QUERY -directive @a(as: String) on FIELD1 +directive @a(as: String) on QUERY -directive @a(as: String = 1) on FIELD1 +directive @a(as: String = 1) on QUERY -directive @a(as: String, b: Int!) on FIELD1 +directive @a(as: String, b: Int!) on QUERY -directive @a(as: String! = 1 @deprecated) on FIELD1 +directive @a(as: String! = 1 @deprecated) on QUERY -directive @a(as: String! = 1 @deprecated) on FIELD1 | FIELD2 +directive @a(as: String! = 1 @deprecated) on QUERY | MUTATION `; diff --git a/tests/graphql_directive_decl/directive_decl.graphql b/tests/graphql_directive_decl/directive_decl.graphql index 6f93dffe..4f60a34a 100644 --- a/tests/graphql_directive_decl/directive_decl.graphql +++ b/tests/graphql_directive_decl/directive_decl.graphql @@ -1,6 +1,6 @@ -directive @a on FIELD1 -directive @a(as: String) on FIELD1 -directive @a(as: String = 1) on FIELD1 -directive @a(as: String, b: Int!) on FIELD1 -directive @a(as: String! = 1 @deprecated) on FIELD1 -directive @a(as: String! = 1 @deprecated) on FIELD1 | FIELD2 +directive @a on QUERY +directive @a(as: String) on QUERY +directive @a(as: String = 1) on QUERY +directive @a(as: String, b: Int!) on QUERY +directive @a(as: String! = 1 @deprecated) on QUERY +directive @a(as: String! = 1 @deprecated) on QUERY | MUTATION diff --git a/tests/graphql_interface/__snapshots__/jsfmt.spec.js.snap b/tests/graphql_interface/__snapshots__/jsfmt.spec.js.snap index a7efd6de..9423d16c 100644 --- a/tests/graphql_interface/__snapshots__/jsfmt.spec.js.snap +++ b/tests/graphql_interface/__snapshots__/jsfmt.spec.js.snap @@ -12,3 +12,147 @@ interface Actor { } `; + +exports[`object_type_def.graphql 1`] = ` +type Artist implements Node & Entity { + # The ID of an object + id: ID! + + # The MBID of the entity. + mbid: MBID! + + # A list of recordings linked to this entity. + recordings(after: String, first: Int): RecordingConnection + + # A list of releases linked to this entity. + releases( + # Filter by one or more release group types. + type: [ReleaseGroupType] + + # Filter by one or more release statuses. + status: [ReleaseStatus] + after: String + first: Int + ): ReleaseConnection + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type Artist implements Node & Entity { + # The ID of an object + id: ID! + + # The MBID of the entity. + mbid: MBID! + + # A list of recordings linked to this entity. + recordings(after: String, first: Int): RecordingConnection + + # A list of releases linked to this entity. + releases( + # Filter by one or more release group types. + type: [ReleaseGroupType] + + # Filter by one or more release statuses. + status: [ReleaseStatus] + after: String + first: Int + ): ReleaseConnection +} + +`; + +exports[`object_type_def_mixed_syntax.graphql 1`] = ` +type MixedArtist implements Node, Entity & Releasable { + # The ID of an object + id: ID! + + # The MBID of the entity. + mbid: MBID! + + # A list of recordings linked to this entity. + recordings(after: String, first: Int): RecordingConnection + + # A list of releases linked to this entity. + releases( + # Filter by one or more release group types. + type: [ReleaseGroupType] + + # Filter by one or more release statuses. + status: [ReleaseStatus] + after: String + first: Int + ): ReleaseConnection + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type MixedArtist implements Node & Entity & Releasable { + # The ID of an object + id: ID! + + # The MBID of the entity. + mbid: MBID! + + # A list of recordings linked to this entity. + recordings(after: String, first: Int): RecordingConnection + + # A list of releases linked to this entity. + releases( + # Filter by one or more release group types. + type: [ReleaseGroupType] + + # Filter by one or more release statuses. + status: [ReleaseStatus] + after: String + first: Int + ): ReleaseConnection +} + +`; + +exports[`object_type_def_old_syntax.graphql 1`] = ` +type OldArtist implements Node, Entity { + # The ID of an object + id: ID! + + # The MBID of the entity. + mbid: MBID! + + # A list of recordings linked to this entity. + recordings(after: String, first: Int): RecordingConnection + + # A list of releases linked to this entity. + releases( + # Filter by one or more release group types. + type: [ReleaseGroupType] + + # Filter by one or more release statuses. + status: [ReleaseStatus] + after: String + first: Int + ): ReleaseConnection + +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type OldArtist implements Node, Entity { + # The ID of an object + id: ID! + + # The MBID of the entity. + mbid: MBID! + + # A list of recordings linked to this entity. + recordings(after: String, first: Int): RecordingConnection + + # A list of releases linked to this entity. + releases( + # Filter by one or more release group types. + type: [ReleaseGroupType] + + # Filter by one or more release statuses. + status: [ReleaseStatus] + after: String + first: Int + ): ReleaseConnection +} + +`; diff --git a/tests/graphql_interface/object_type_def.graphql b/tests/graphql_interface/object_type_def.graphql new file mode 100644 index 00000000..e1f76919 --- /dev/null +++ b/tests/graphql_interface/object_type_def.graphql @@ -0,0 +1,22 @@ +type Artist implements Node & Entity { + # The ID of an object + id: ID! + + # The MBID of the entity. + mbid: MBID! + + # A list of recordings linked to this entity. + recordings(after: String, first: Int): RecordingConnection + + # A list of releases linked to this entity. + releases( + # Filter by one or more release group types. + type: [ReleaseGroupType] + + # Filter by one or more release statuses. + status: [ReleaseStatus] + after: String + first: Int + ): ReleaseConnection + +} diff --git a/tests/graphql_interface/object_type_def_mixed_syntax.graphql b/tests/graphql_interface/object_type_def_mixed_syntax.graphql new file mode 100644 index 00000000..9071bbe3 --- /dev/null +++ b/tests/graphql_interface/object_type_def_mixed_syntax.graphql @@ -0,0 +1,22 @@ +type MixedArtist implements Node, Entity & Releasable { + # The ID of an object + id: ID! + + # The MBID of the entity. + mbid: MBID! + + # A list of recordings linked to this entity. + recordings(after: String, first: Int): RecordingConnection + + # A list of releases linked to this entity. + releases( + # Filter by one or more release group types. + type: [ReleaseGroupType] + + # Filter by one or more release statuses. + status: [ReleaseStatus] + after: String + first: Int + ): ReleaseConnection + +} diff --git a/tests/graphql_interface/object_type_def_old_syntax.graphql b/tests/graphql_interface/object_type_def_old_syntax.graphql new file mode 100644 index 00000000..a60f7bec --- /dev/null +++ b/tests/graphql_interface/object_type_def_old_syntax.graphql @@ -0,0 +1,22 @@ +type OldArtist implements Node, Entity { + # The ID of an object + id: ID! + + # The MBID of the entity. + mbid: MBID! + + # A list of recordings linked to this entity. + recordings(after: String, first: Int): RecordingConnection + + # A list of releases linked to this entity. + releases( + # Filter by one or more release group types. + type: [ReleaseGroupType] + + # Filter by one or more release statuses. + status: [ReleaseStatus] + after: String + first: Int + ): ReleaseConnection + +} diff --git a/tests/graphql_kitchen_sink/__snapshots__/jsfmt.spec.js.snap b/tests/graphql_kitchen_sink/__snapshots__/jsfmt.spec.js.snap index 1fe61eb7..72d49926 100644 --- a/tests/graphql_kitchen_sink/__snapshots__/jsfmt.spec.js.snap +++ b/tests/graphql_kitchen_sink/__snapshots__/jsfmt.spec.js.snap @@ -1,6 +1,11 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`kitchen_sink.graphql 1`] = ` +# Copyright (c) 2015-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + query queryName($foo: ComplexType, $site: Site = MOBILE) { whoever123is: node(id: [123, 456]) { id , @@ -44,18 +49,21 @@ subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) { } fragment frag on Friend { - foo(size: $size, bar: $b, obj: {key: "value"}) + foo(size: $size, bar: $b, obj: {key: "value", block: """ + block string uses \\""" + """}) } { unnamed(truthy: true, falsey: false, nullish: null), query } - -query { - field -} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Copyright (c) 2015-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + query queryName($foo: ComplexType, $site: Site = MOBILE) { whoever123is: node(id: [123, 456]) { id @@ -99,7 +107,16 @@ subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) { } fragment frag on Friend { - foo(size: $size, bar: $b, obj: { key: "value" }) + foo( + size: $size + bar: $b + obj: { + key: "value" + block: """ + block string uses \\""" + """ + } + ) } { @@ -107,8 +124,243 @@ fragment frag on Friend { query } -query { - field +`; + +exports[`schema_kitchen_sink.graphql 1`] = ` +# Copyright (c) 2015-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +schema { + query: QueryType + mutation: MutationType } +""" +This is a description +of the \`Foo\` type. +""" +type Foo implements Bar { + one: Type + two(argument: InputType!): Type + three(argument: InputType, other: String): Int + four(argument: String = "string"): String + five(argument: [String] = ["string", "string"]): String + six(argument: InputType = {key: "value"}): Type + seven(argument: Int = null): Type +} + +type AnnotatedObject @onObject(arg: "value") { + annotatedField(arg: Type = "default" @onArg): Type @onField +} + +type UndefinedType + +extend type Foo { + seven(argument: [String]): Type +} + +extend type Foo @onType + +""" +This is a description +""" +interface Bar { + one: Type + four(argument: String = "string"): String +} + +interface AnnotatedInterface @onInterface { + annotatedField(arg: Type @onArg): Type @onField +} + +interface UndefinedInterface + +extend interface Bar { + two(argument: InputType!): Type +} + +extend interface Bar @onInterface + +union Feed = Story | Article | Advert + +union AnnotatedUnion @onUnion = A | B + +union AnnotatedUnionTwo @onUnion = | A | B + +union UndefinedUnion + +extend union Feed = Photo | Video + +extend union Feed @onUnion + +scalar CustomScalar + +scalar AnnotatedScalar @onScalar + +extend scalar CustomScalar @onScalar + +enum Site { + DESKTOP + MOBILE +} + +enum AnnotatedEnum @onEnum { + ANNOTATED_VALUE @onEnumValue + OTHER_VALUE +} + +enum UndefinedEnum + +extend enum Site { + VR +} + +extend enum Site @onEnum + +input InputType { + key: String! + answer: Int = 42 +} + +input AnnotatedInput @onInputObject { + annotatedField: Type @onField +} + +input UndefinedInput + +extend input InputType { + other: Float = 1.23e4 +} + +extend input InputType @onInputObject + +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +directive @include(if: Boolean!) + on FIELD + | FRAGMENT_SPREAD + | INLINE_FRAGMENT + +directive @include2(if: Boolean!) on + | FIELD + | FRAGMENT_SPREAD + | INLINE_FRAGMENT +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +# Copyright (c) 2015-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +schema { + query: QueryType + mutation: MutationType +} + +""" +This is a description +of the \`Foo\` type. +""" +type Foo implements Bar { + one: Type + two(argument: InputType!): Type + three(argument: InputType, other: String): Int + four(argument: String = "string"): String + five(argument: [String] = ["string", "string"]): String + six(argument: InputType = { key: "value" }): Type + seven(argument: Int = null): Type +} + +type AnnotatedObject @onObject(arg: "value") { + annotatedField(arg: Type = "default" @onArg): Type @onField +} + +type UndefinedType + +extend type Foo { + seven(argument: [String]): Type +} + +extend type Foo @onType + +""" +This is a description +""" +interface Bar { + one: Type + four(argument: String = "string"): String +} + +interface AnnotatedInterface @onInterface { + annotatedField(arg: Type @onArg): Type @onField +} + +interface UndefinedInterface + +extend interface Bar { + two(argument: InputType!): Type +} + +extend interface Bar @onInterface + +union Feed = Story | Article | Advert + +union AnnotatedUnion @onUnion = A | B + +union AnnotatedUnionTwo @onUnion = A | B + +union UndefinedUnion + +extend union Feed = Photo | Video + +extend union Feed @onUnion + +scalar CustomScalar + +scalar AnnotatedScalar @onScalar + +extend scalar CustomScalar @onScalar + +enum Site { + DESKTOP + MOBILE +} + +enum AnnotatedEnum @onEnum { + ANNOTATED_VALUE @onEnumValue + OTHER_VALUE +} + +enum UndefinedEnum + +extend enum Site { + VR +} + +extend enum Site @onEnum + +input InputType { + key: String! + answer: Int = 42 +} + +input AnnotatedInput @onInputObject { + annotatedField: Type @onField +} + +input UndefinedInput + +extend input InputType { + other: Float = 1.23e4 +} + +extend input InputType @onInputObject + +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +directive @include(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +directive @include2(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + `; diff --git a/tests/graphql_kitchen_sink/kitchen_sink.graphql b/tests/graphql_kitchen_sink/kitchen_sink.graphql index 5f0900e8..7b7908a4 100644 --- a/tests/graphql_kitchen_sink/kitchen_sink.graphql +++ b/tests/graphql_kitchen_sink/kitchen_sink.graphql @@ -1,3 +1,8 @@ +# Copyright (c) 2015-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + query queryName($foo: ComplexType, $site: Site = MOBILE) { whoever123is: node(id: [123, 456]) { id , @@ -41,14 +46,12 @@ subscription StoryLikeSubscription($input: StoryLikeSubscribeInput) { } fragment frag on Friend { - foo(size: $size, bar: $b, obj: {key: "value"}) + foo(size: $size, bar: $b, obj: {key: "value", block: """ + block string uses \""" + """}) } { unnamed(truthy: true, falsey: false, nullish: null), query } - -query { - field -} diff --git a/tests/graphql_kitchen_sink/schema_kitchen_sink.graphql b/tests/graphql_kitchen_sink/schema_kitchen_sink.graphql new file mode 100644 index 00000000..0c72dcca --- /dev/null +++ b/tests/graphql_kitchen_sink/schema_kitchen_sink.graphql @@ -0,0 +1,120 @@ +# Copyright (c) 2015-present, Facebook, Inc. +# +# This source code is licensed under the MIT license found in the +# LICENSE file in the root directory of this source tree. + +schema { + query: QueryType + mutation: MutationType +} + +""" +This is a description +of the `Foo` type. +""" +type Foo implements Bar { + one: Type + two(argument: InputType!): Type + three(argument: InputType, other: String): Int + four(argument: String = "string"): String + five(argument: [String] = ["string", "string"]): String + six(argument: InputType = {key: "value"}): Type + seven(argument: Int = null): Type +} + +type AnnotatedObject @onObject(arg: "value") { + annotatedField(arg: Type = "default" @onArg): Type @onField +} + +type UndefinedType + +extend type Foo { + seven(argument: [String]): Type +} + +extend type Foo @onType + +""" +This is a description +""" +interface Bar { + one: Type + four(argument: String = "string"): String +} + +interface AnnotatedInterface @onInterface { + annotatedField(arg: Type @onArg): Type @onField +} + +interface UndefinedInterface + +extend interface Bar { + two(argument: InputType!): Type +} + +extend interface Bar @onInterface + +union Feed = Story | Article | Advert + +union AnnotatedUnion @onUnion = A | B + +union AnnotatedUnionTwo @onUnion = | A | B + +union UndefinedUnion + +extend union Feed = Photo | Video + +extend union Feed @onUnion + +scalar CustomScalar + +scalar AnnotatedScalar @onScalar + +extend scalar CustomScalar @onScalar + +enum Site { + DESKTOP + MOBILE +} + +enum AnnotatedEnum @onEnum { + ANNOTATED_VALUE @onEnumValue + OTHER_VALUE +} + +enum UndefinedEnum + +extend enum Site { + VR +} + +extend enum Site @onEnum + +input InputType { + key: String! + answer: Int = 42 +} + +input AnnotatedInput @onInputObject { + annotatedField: Type @onField +} + +input UndefinedInput + +extend input InputType { + other: Float = 1.23e4 +} + +extend input InputType @onInputObject + +directive @skip(if: Boolean!) on FIELD | FRAGMENT_SPREAD | INLINE_FRAGMENT + +directive @include(if: Boolean!) + on FIELD + | FRAGMENT_SPREAD + | INLINE_FRAGMENT + +directive @include2(if: Boolean!) on + | FIELD + | FRAGMENT_SPREAD + | INLINE_FRAGMENT diff --git a/tests/graphql_newline/__snapshots__/jsfmt.spec.js.snap b/tests/graphql_newline/__snapshots__/jsfmt.spec.js.snap index 7efb263d..c3c18861 100644 --- a/tests/graphql_newline/__snapshots__/jsfmt.spec.js.snap +++ b/tests/graphql_newline/__snapshots__/jsfmt.spec.js.snap @@ -10,7 +10,7 @@ directive @dir( arg2: String arg3: String -) on Field +) on QUERY ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ directive @dir( # comment @@ -19,7 +19,7 @@ directive @dir( # comment arg2: String arg3: String -) on Field +) on QUERY `; diff --git a/tests/graphql_newline/directive_decl.graphql b/tests/graphql_newline/directive_decl.graphql index cee08569..23fab7d4 100644 --- a/tests/graphql_newline/directive_decl.graphql +++ b/tests/graphql_newline/directive_decl.graphql @@ -7,4 +7,4 @@ directive @dir( arg2: String arg3: String -) on Field +) on QUERY diff --git a/tests/graphql_object_type_def/__snapshots__/jsfmt.spec.js.snap b/tests/graphql_object_type_def/__snapshots__/jsfmt.spec.js.snap index 6ca886fb..98e1ab0a 100644 --- a/tests/graphql_object_type_def/__snapshots__/jsfmt.spec.js.snap +++ b/tests/graphql_object_type_def/__snapshots__/jsfmt.spec.js.snap @@ -35,9 +35,11 @@ extend type Feedback { exports[`implements.graphql 1`] = ` type VRMConversation implements Node, Entity @foo { + a: Int } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ type VRMConversation implements Node, Entity @foo { + a: Int } `; diff --git a/tests/graphql_object_type_def/implements.graphql b/tests/graphql_object_type_def/implements.graphql index 62e65f89..3f813586 100644 --- a/tests/graphql_object_type_def/implements.graphql +++ b/tests/graphql_object_type_def/implements.graphql @@ -1,2 +1,3 @@ type VRMConversation implements Node, Entity @foo { + a: Int } diff --git a/tests/graphql_string/__snapshots__/jsfmt.spec.js.snap b/tests/graphql_string/__snapshots__/jsfmt.spec.js.snap index 5b76c164..601e1611 100644 --- a/tests/graphql_string/__snapshots__/jsfmt.spec.js.snap +++ b/tests/graphql_string/__snapshots__/jsfmt.spec.js.snap @@ -2,9 +2,115 @@ exports[`string.graphql 1`] = ` query X($a: Int) @relay(meta: "{\\"lowPri\\": true}") { a } + +"""abc""" +type T { + a: Int +} + +""" +abc +""" +type T { + a: Int +} + +""" + a + b + c + """ +type T { a: Int } + +type Foo { +""" +This is a description +of the \`one\` field. +""" + one: Type +} + +type Foo { +q("docs" field: String): Type +q("A long string of description of the field parameter to make this break" field: String): Type +q("""docs""" field: String): Type +} + +enum Enum { +""" +Description of \`one\` +""" +one +} + +input Input { +""" +Description of \`one\` +""" +one: string +} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ query X($a: Int) @relay(meta: "{\\"lowPri\\": true}") { a } +""" +abc +""" +type T { + a: Int +} + +""" +abc +""" +type T { + a: Int +} + +""" +a + b + c +""" +type T { + a: Int +} + +type Foo { + """ + This is a description + of the \`one\` field. + """ + one: Type +} + +type Foo { + q("docs" field: String): Type + q( + "A long string of description of the field parameter to make this break" + field: String + ): Type + q( + """ + docs + """ + field: String + ): Type +} + +enum Enum { + """ + Description of \`one\` + """ + one +} + +input Input { + """ + Description of \`one\` + """ + one: string +} + `; diff --git a/tests/graphql_string/string.graphql b/tests/graphql_string/string.graphql index b33cefef..dea48a4a 100644 --- a/tests/graphql_string/string.graphql +++ b/tests/graphql_string/string.graphql @@ -1 +1,48 @@ query X($a: Int) @relay(meta: "{\"lowPri\": true}") { a } + +"""abc""" +type T { + a: Int +} + +""" +abc +""" +type T { + a: Int +} + +""" + a + b + c + """ +type T { a: Int } + +type Foo { +""" +This is a description +of the `one` field. +""" + one: Type +} + +type Foo { +q("docs" field: String): Type +q("A long string of description of the field parameter to make this break" field: String): Type +q("""docs""" field: String): Type +} + +enum Enum { +""" +Description of `one` +""" +one +} + +input Input { +""" +Description of `one` +""" +one: string +} diff --git a/tests/html_aurelia/__snapshots__/jsfmt.spec.js.snap b/tests/html_aurelia/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..f74a8457 --- /dev/null +++ b/tests/html_aurelia/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,15 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`basic.html 1`] = ` + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +`; diff --git a/tests/html_aurelia/basic.html b/tests/html_aurelia/basic.html new file mode 100644 index 00000000..2b8dc114 --- /dev/null +++ b/tests/html_aurelia/basic.html @@ -0,0 +1,3 @@ + diff --git a/tests/html_aurelia/jsfmt.spec.js b/tests/html_aurelia/jsfmt.spec.js new file mode 100644 index 00000000..3ebd9a9e --- /dev/null +++ b/tests/html_aurelia/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["parse5"]); diff --git a/tests/html_basics/__snapshots__/jsfmt.spec.js.snap b/tests/html_basics/__snapshots__/jsfmt.spec.js.snap index 902d98bd..78435a2e 100644 --- a/tests/html_basics/__snapshots__/jsfmt.spec.js.snap +++ b/tests/html_basics/__snapshots__/jsfmt.spec.js.snap @@ -42,6 +42,33 @@ exports[`hello-world.html 1`] = ` `; +exports[`html-comments.html 1`] = ` + + + + Anchor + + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + + + + + + + Anchor + -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +{{#if a}} + b +{{else}} + {{#each c as |d|}} + e + {{/each}} +{{/if}} + +{{#if a}} + {{#if b}} + ab + {{else if c}} + ac + {{/if}} +{{/if}}~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ {{#if a}} b {{else if c}} @@ -371,6 +386,20 @@ exports[`else-if.hbs 1`] = ` {{/if}} +{{#if a}} + b +{{else}} + {{#each c as |d|}} + e + {{/each}} +{{/if}} +{{#if a}} + {{#if b}} + ab + {{else if c}} + ac + {{/if}} +{{/if}} `; exports[`literals.hbs 1`] = ` diff --git a/tests/html_glimmer/else-if.hbs b/tests/html_glimmer/else-if.hbs index 813d82ac..10276729 100644 --- a/tests/html_glimmer/else-if.hbs +++ b/tests/html_glimmer/else-if.hbs @@ -51,3 +51,19 @@ {{/if}} + +{{#if a}} + b +{{else}} + {{#each c as |d|}} + e + {{/each}} +{{/if}} + +{{#if a}} + {{#if b}} + ab + {{else if c}} + ac + {{/if}} +{{/if}} \ No newline at end of file diff --git a/tests/if/__snapshots__/jsfmt.spec.js.snap b/tests/if/__snapshots__/jsfmt.spec.js.snap index 50598c4e..d3afd096 100644 --- a/tests/if/__snapshots__/jsfmt.spec.js.snap +++ b/tests/if/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,36 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`comment_before_else.js 1`] = ` +if (cond) { + stuff; +} /* comment */ else if (cond) { + stuff; +} +// comment +else { + stuff; +} + +if (cond) stuff; +// comment +else stuff; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +if (cond) { + stuff; +} /* comment */ else if (cond) { + stuff; +} +// comment +else { + stuff; +} + +if (cond) stuff; +// comment +else stuff; + +`; + exports[`else.js 1`] = ` // Both functions below should be formatted exactly the same diff --git a/tests/if/comment_before_else.js b/tests/if/comment_before_else.js new file mode 100644 index 00000000..d51c2f9a --- /dev/null +++ b/tests/if/comment_before_else.js @@ -0,0 +1,13 @@ +if (cond) { + stuff; +} /* comment */ else if (cond) { + stuff; +} +// comment +else { + stuff; +} + +if (cond) stuff; +// comment +else stuff; diff --git a/tests/insert-pragma/__snapshots__/jsfmt.spec.js.snap b/tests/insert-pragma/js/__snapshots__/jsfmt.spec.js.snap similarity index 90% rename from tests/insert-pragma/__snapshots__/jsfmt.spec.js.snap rename to tests/insert-pragma/js/__snapshots__/jsfmt.spec.js.snap index 38f670e2..0e5928b9 100644 --- a/tests/insert-pragma/__snapshots__/jsfmt.spec.js.snap +++ b/tests/insert-pragma/js/__snapshots__/jsfmt.spec.js.snap @@ -24,6 +24,20 @@ const fruit = "tomatoes"; `; +exports[`module-with-multiple-same-pragma.js 1`] = ` +/** + * @x 1 + * @x 2 + */ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +/** + * @format + * @x 1 + * @x 2 + */ + +`; + exports[`module-with-pragma.js 1`] = ` /** * Here lie words above pragmas diff --git a/tests/insert-pragma/js/jsfmt.spec.js b/tests/insert-pragma/js/jsfmt.spec.js new file mode 100644 index 00000000..4f802b63 --- /dev/null +++ b/tests/insert-pragma/js/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["flow", "babylon", "typescript"], { insertPragma: true }); diff --git a/tests/insert-pragma/module-with-late-docblock.js b/tests/insert-pragma/js/module-with-late-docblock.js similarity index 100% rename from tests/insert-pragma/module-with-late-docblock.js rename to tests/insert-pragma/js/module-with-late-docblock.js diff --git a/tests/insert-pragma/js/module-with-multiple-same-pragma.js b/tests/insert-pragma/js/module-with-multiple-same-pragma.js new file mode 100644 index 00000000..e41dbf6c --- /dev/null +++ b/tests/insert-pragma/js/module-with-multiple-same-pragma.js @@ -0,0 +1,4 @@ +/** + * @x 1 + * @x 2 + */ diff --git a/tests/insert-pragma/module-with-pragma.js b/tests/insert-pragma/js/module-with-pragma.js similarity index 100% rename from tests/insert-pragma/module-with-pragma.js rename to tests/insert-pragma/js/module-with-pragma.js diff --git a/tests/insert-pragma/module-without-docblock.js b/tests/insert-pragma/js/module-without-docblock.js similarity index 100% rename from tests/insert-pragma/module-without-docblock.js rename to tests/insert-pragma/js/module-without-docblock.js diff --git a/tests/insert-pragma/module-without-pragma.js b/tests/insert-pragma/js/module-without-pragma.js similarity index 100% rename from tests/insert-pragma/module-without-pragma.js rename to tests/insert-pragma/js/module-without-pragma.js diff --git a/tests/insert-pragma/trailing-spaces-first-line.js b/tests/insert-pragma/js/trailing-spaces-first-line.js similarity index 100% rename from tests/insert-pragma/trailing-spaces-first-line.js rename to tests/insert-pragma/js/trailing-spaces-first-line.js diff --git a/tests/insert-pragma/jsfmt.spec.js b/tests/insert-pragma/jsfmt.spec.js deleted file mode 100644 index ff094ca9..00000000 --- a/tests/insert-pragma/jsfmt.spec.js +++ /dev/null @@ -1 +0,0 @@ -run_spec(__dirname, ["flow"], { insertPragma: true }); diff --git a/tests/insert-pragma/markdown/__snapshots__/jsfmt.spec.js.snap b/tests/insert-pragma/markdown/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..7d268db8 --- /dev/null +++ b/tests/insert-pragma/markdown/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,52 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`front-matter.md 1`] = ` +--- +something +--- + +# hello world + +some content some content some content some content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +something +--- + + + +# hello world + +some content some content some content some content + +`; + +exports[`simple.md 1`] = ` +# hello world + +some content some content some content some content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +# hello world + +some content some content some content some content + +`; + +exports[`start-with-comment.md 1`] = ` + + +# hello world + +some content some content some content some content +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +# hello world + +some content some content some content some content + +`; diff --git a/tests/insert-pragma/markdown/front-matter.md b/tests/insert-pragma/markdown/front-matter.md new file mode 100644 index 00000000..724c2436 --- /dev/null +++ b/tests/insert-pragma/markdown/front-matter.md @@ -0,0 +1,7 @@ +--- +something +--- + +# hello world + +some content some content some content some content diff --git a/tests/insert-pragma/markdown/jsfmt.spec.js b/tests/insert-pragma/markdown/jsfmt.spec.js new file mode 100644 index 00000000..a1f27d6d --- /dev/null +++ b/tests/insert-pragma/markdown/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["markdown"], { insertPragma: true }); diff --git a/tests/insert-pragma/markdown/simple.md b/tests/insert-pragma/markdown/simple.md new file mode 100644 index 00000000..32a36973 --- /dev/null +++ b/tests/insert-pragma/markdown/simple.md @@ -0,0 +1,3 @@ +# hello world + +some content some content some content some content diff --git a/tests/insert-pragma/markdown/start-with-comment.md b/tests/insert-pragma/markdown/start-with-comment.md new file mode 100644 index 00000000..85eccd4d --- /dev/null +++ b/tests/insert-pragma/markdown/start-with-comment.md @@ -0,0 +1,5 @@ + + +# hello world + +some content some content some content some content diff --git a/tests/interface/__snapshots__/jsfmt.spec.js.snap b/tests/interface/__snapshots__/jsfmt.spec.js.snap index c9b5219d..29530783 100644 --- a/tests/interface/__snapshots__/jsfmt.spec.js.snap +++ b/tests/interface/__snapshots__/jsfmt.spec.js.snap @@ -12,6 +12,10 @@ export class Environment2 extends GenericEnvironment< SomeType, AnotherType, YetAnotherType, + DifferentType1, + DifferentType2, + DifferentType3, + DifferentType4, > { m() {}; }; @@ -23,7 +27,11 @@ export interface Environment1 export class Environment2 extends GenericEnvironment< SomeType, AnotherType, - YetAnotherType + YetAnotherType, + DifferentType1, + DifferentType2, + DifferentType3, + DifferentType4 > { m() {} } diff --git a/tests/interface/break.js b/tests/interface/break.js index f6b33dfb..02e9e1e1 100644 --- a/tests/interface/break.js +++ b/tests/interface/break.js @@ -9,6 +9,10 @@ export class Environment2 extends GenericEnvironment< SomeType, AnotherType, YetAnotherType, + DifferentType1, + DifferentType2, + DifferentType3, + DifferentType4, > { m() {}; }; diff --git a/tests/jsx-attr-element/__snapshots__/jsfmt.spec.js.snap b/tests/jsx-attr-element/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..c1ec5a95 --- /dev/null +++ b/tests/jsx-attr-element/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`attr-element.js 1`] = ` + />; + />; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +/>; + + + + + +/>; + +`; diff --git a/tests/jsx-attr-element/attr-element.js b/tests/jsx-attr-element/attr-element.js new file mode 100644 index 00000000..f31bd230 --- /dev/null +++ b/tests/jsx-attr-element/attr-element.js @@ -0,0 +1,2 @@ + />; + />; diff --git a/tests/jsx-attr-element/jsfmt.spec.js b/tests/jsx-attr-element/jsfmt.spec.js new file mode 100644 index 00000000..ad02d22b --- /dev/null +++ b/tests/jsx-attr-element/jsfmt.spec.js @@ -0,0 +1,2 @@ +// Flow and TypeScript don't support JSX elements as attribute values +run_spec(__dirname, ["babylon"]); diff --git a/tests/jsx/__snapshots__/jsfmt.spec.js.snap b/tests/jsx/__snapshots__/jsfmt.spec.js.snap index 1fcc1f7c..50dc573e 100644 --- a/tests/jsx/__snapshots__/jsfmt.spec.js.snap +++ b/tests/jsx/__snapshots__/jsfmt.spec.js.snap @@ -12,6 +12,13 @@ exports[`attr-comments.js 1`] = ` // comment arg => fn(arg) } + propArrowWithBreak={ + // comment + arg => + fn({ + makeItBreak + }) + } propArray={ // comment [el1, el2] @@ -37,6 +44,13 @@ exports[`attr-comments.js 1`] = ` // comment arg => fn(arg) } + propArrowWithBreak={ + // comment + arg => + fn({ + makeItBreak + }) + } propArray={ // comment [el1, el2] diff --git a/tests/jsx/attr-comments.js b/tests/jsx/attr-comments.js index 1fc74d0a..236c6f29 100644 --- a/tests/jsx/attr-comments.js +++ b/tests/jsx/attr-comments.js @@ -9,6 +9,13 @@ // comment arg => fn(arg) } + propArrowWithBreak={ + // comment + arg => + fn({ + makeItBreak + }) + } propArray={ // comment [el1, el2] diff --git a/tests/jsx_last_line/__snapshots__/jsfmt.spec.js.snap b/tests/jsx_last_line/__snapshots__/jsfmt.spec.js.snap index 0ac63048..369b1104 100644 --- a/tests/jsx_last_line/__snapshots__/jsfmt.spec.js.snap +++ b/tests/jsx_last_line/__snapshots__/jsfmt.spec.js.snap @@ -48,3 +48,48 @@ exports[`last_line.js 2`] = ` ; `; + +exports[`single_prop_multiline_string.js 1`] = ` +; + +Content; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +; + + + Content +; + +`; + +exports[`single_prop_multiline_string.js 2`] = ` +; + +Content; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +; + + + Content +; + +`; diff --git a/tests/jsx_last_line/single_prop_multiline_string.js b/tests/jsx_last_line/single_prop_multiline_string.js new file mode 100644 index 00000000..54173322 --- /dev/null +++ b/tests/jsx_last_line/single_prop_multiline_string.js @@ -0,0 +1,6 @@ +; + +Content; diff --git a/tests/jsx_spread/jsfmt.spec.js b/tests/jsx_spread/jsfmt.spec.js index 968651cd..bffb7a54 100644 --- a/tests/jsx_spread/jsfmt.spec.js +++ b/tests/jsx_spread/jsfmt.spec.js @@ -1 +1 @@ -run_spec(__dirname, ["babylon"]); +run_spec(__dirname, ["babylon", "flow"]); diff --git a/tests/markdown/__snapshots__/jsfmt.spec.js.snap b/tests/markdown/__snapshots__/jsfmt.spec.js.snap index c473759b..513bbd2d 100644 --- a/tests/markdown/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown/__snapshots__/jsfmt.spec.js.snap @@ -14,7 +14,7 @@ Prettier is an opinionated code formatter with support for: * [JSX](https://facebook.github.io/jsx/) * [Flow](https://flow.org/) * [TypeScript](https://www.typescriptlang.org/) -* CSS, [LESS](http://lesscss.org/), and [SCSS](http://sass-lang.com) +* CSS, [Less](http://lesscss.org/), and [SCSS](http://sass-lang.com) * [JSON](http://json.org/) * [GraphQL](http://graphql.org/) @@ -414,7 +414,7 @@ Copy the following config into your \`.pre-commit-config.yaml\` file: \`\`\` -Find more info from [here](http://pre-commit.com). +Find more info from [here](https://pre-commit.com). ##### Option 3. bash script @@ -836,7 +836,7 @@ features enabled, but you can also use the All of JSX and Flow syntax is supported. In fact, the test suite in \`tests/flow\` *is* the entire Flow test suite and they all pass. -Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, [LESS](http://lesscss.org/), [SCSS](http://sass-lang.com), [JSON](http://json.org/), and [GraphQL](http://graphql.org/). +Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, [Less](http://lesscss.org/), [SCSS](http://sass-lang.com), [JSON](http://json.org/), and [GraphQL](http://graphql.org/). The minimum version of TypeScript supported is 2.1.3 as it introduces the ability to have leading \`|\` for type definitions which prettier outputs. @@ -914,7 +914,7 @@ Prettier is an opinionated code formatter with support for: * [JSX](https://facebook.github.io/jsx/) * [Flow](https://flow.org/) * [TypeScript](https://www.typescriptlang.org/) -* CSS, [LESS](http://lesscss.org/), and [SCSS](http://sass-lang.com) +* CSS, [Less](http://lesscss.org/), and [SCSS](http://sass-lang.com) * [JSON](http://json.org/) * [GraphQL](http://graphql.org/) @@ -1398,7 +1398,7 @@ Copy the following config into your \`.pre-commit-config.yaml\` file: - id: prettier \`\`\` -Find more info from [here](http://pre-commit.com). +Find more info from [here](https://pre-commit.com). ##### Option 3. bash script @@ -1890,7 +1890,7 @@ All of JSX and Flow syntax is supported. In fact, the test suite in \`tests/flow _is_ the entire Flow test suite and they all pass. Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, -[LESS](http://lesscss.org/), [SCSS](http://sass-lang.com), +[Less](http://lesscss.org/), [SCSS](http://sass-lang.com), [JSON](http://json.org/), and [GraphQL](http://graphql.org/). The minimum version of TypeScript supported is 2.1.3 as it introduces the @@ -1985,7 +1985,7 @@ Prettier is an opinionated code formatter with support for: * [JSX](https://facebook.github.io/jsx/) * [Flow](https://flow.org/) * [TypeScript](https://www.typescriptlang.org/) -* CSS, [LESS](http://lesscss.org/), and [SCSS](http://sass-lang.com) +* CSS, [Less](http://lesscss.org/), and [SCSS](http://sass-lang.com) * [JSON](http://json.org/) * [GraphQL](http://graphql.org/) @@ -2385,7 +2385,7 @@ Copy the following config into your \`.pre-commit-config.yaml\` file: \`\`\` -Find more info from [here](http://pre-commit.com). +Find more info from [here](https://pre-commit.com). ##### Option 3. bash script @@ -2807,7 +2807,7 @@ features enabled, but you can also use the All of JSX and Flow syntax is supported. In fact, the test suite in \`tests/flow\` *is* the entire Flow test suite and they all pass. -Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, [LESS](http://lesscss.org/), [SCSS](http://sass-lang.com), [JSON](http://json.org/), and [GraphQL](http://graphql.org/). +Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, [Less](http://lesscss.org/), [SCSS](http://sass-lang.com), [JSON](http://json.org/), and [GraphQL](http://graphql.org/). The minimum version of TypeScript supported is 2.1.3 as it introduces the ability to have leading \`|\` for type definitions which prettier outputs. @@ -2885,7 +2885,7 @@ Prettier is an opinionated code formatter with support for: * [JSX](https://facebook.github.io/jsx/) * [Flow](https://flow.org/) * [TypeScript](https://www.typescriptlang.org/) -* CSS, [LESS](http://lesscss.org/), and [SCSS](http://sass-lang.com) +* CSS, [Less](http://lesscss.org/), and [SCSS](http://sass-lang.com) * [JSON](http://json.org/) * [GraphQL](http://graphql.org/) @@ -3369,7 +3369,7 @@ Copy the following config into your \`.pre-commit-config.yaml\` file: - id: prettier \`\`\` -Find more info from [here](http://pre-commit.com). +Find more info from [here](https://pre-commit.com). ##### Option 3. bash script @@ -3861,7 +3861,7 @@ All of JSX and Flow syntax is supported. In fact, the test suite in \`tests/flow _is_ the entire Flow test suite and they all pass. Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, -[LESS](http://lesscss.org/), [SCSS](http://sass-lang.com), +[Less](http://lesscss.org/), [SCSS](http://sass-lang.com), [JSON](http://json.org/), and [GraphQL](http://graphql.org/). The minimum version of TypeScript supported is 2.1.3 as it introduces the @@ -4207,8 +4207,8 @@ This is a paragraph. > ## This is a header. > -> 1. This is the first list item. -> 2. This is the second list item. +> 1. This is the first list item. +> 2. This is the second list item. > > Here's some example code: > @@ -4248,14 +4248,14 @@ This is a paragraph. * Blue \`\`\` -1. Buy flour and salt -1. Mix together with water -1. Bake +1. Buy flour and salt +1. Mix together with water +1. Bake \`\`\`markdown -1. Buy flour and salt -1. Mix together with water -1. Bake +1. Buy flour and salt +1. Mix together with water +1. Bake \`\`\` Paragraph: @@ -4595,8 +4595,8 @@ This is a paragraph. > ## This is a header. > -> 1. This is the first list item. -> 2. This is the second list item. +> 1. This is the first list item. +> 2. This is the second list item. > > Here's some example code: > @@ -4636,14 +4636,14 @@ This is a paragraph. * Blue \`\`\` -1. Buy flour and salt -1. Mix together with water -1. Bake +1. Buy flour and salt +1. Mix together with water +1. Bake \`\`\`markdown -1. Buy flour and salt -1. Mix together with water -1. Bake +1. Buy flour and salt +1. Mix together with water +1. Bake \`\`\` Paragraph: diff --git a/tests/markdown/real-world-case.md b/tests/markdown/real-world-case.md index 8e2747d0..89badcc6 100644 --- a/tests/markdown/real-world-case.md +++ b/tests/markdown/real-world-case.md @@ -11,7 +11,7 @@ Prettier is an opinionated code formatter with support for: * [JSX](https://facebook.github.io/jsx/) * [Flow](https://flow.org/) * [TypeScript](https://www.typescriptlang.org/) -* CSS, [LESS](http://lesscss.org/), and [SCSS](http://sass-lang.com) +* CSS, [Less](http://lesscss.org/), and [SCSS](http://sass-lang.com) * [JSON](http://json.org/) * [GraphQL](http://graphql.org/) @@ -411,7 +411,7 @@ Copy the following config into your `.pre-commit-config.yaml` file: ``` -Find more info from [here](http://pre-commit.com). +Find more info from [here](https://pre-commit.com). ##### Option 3. bash script @@ -833,7 +833,7 @@ features enabled, but you can also use the All of JSX and Flow syntax is supported. In fact, the test suite in `tests/flow` *is* the entire Flow test suite and they all pass. -Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, [LESS](http://lesscss.org/), [SCSS](http://sass-lang.com), [JSON](http://json.org/), and [GraphQL](http://graphql.org/). +Prettier also supports [TypeScript](https://www.typescriptlang.org/), CSS, [Less](http://lesscss.org/), [SCSS](http://sass-lang.com), [JSON](http://json.org/), and [GraphQL](http://graphql.org/). The minimum version of TypeScript supported is 2.1.3 as it introduces the ability to have leading `|` for type definitions which prettier outputs. diff --git a/tests/markdown_code/0-indent-js.md b/tests/markdown_code/0-indent-js.md new file mode 100644 index 00000000..1e8f10dc --- /dev/null +++ b/tests/markdown_code/0-indent-js.md @@ -0,0 +1,20 @@ +- 1 + - 2 + - 3 + ```js + md` + # this is the root indent + + # this is the root indent + + # this is the root indent + ` + + something` + asd + + asd + + asd + ` + ``` diff --git a/tests/markdown_code/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_code/__snapshots__/jsfmt.spec.js.snap index 64974b2f..b00c9e1b 100644 --- a/tests/markdown_code/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_code/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,53 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`0-indent-js.md 1`] = ` +- 1 + - 2 + - 3 + \`\`\`js + md\` + # this is the root indent + + # this is the root indent + + # this is the root indent + \` + + something\` + asd + + asd + + asd + \` + \`\`\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 1 + + * 2 + + * 3 + + \`\`\`js + md\` + # this is the root indent + + # this is the root indent + + # this is the root indent + \`; + + something\` + asd + + asd + + asd + \`; + \`\`\` + +`; + exports[`additional-space.md 1`] = ` 1. Fork the repo and create your branch from \`master\`. @@ -23,31 +71,31 @@ exports[`additional-space.md 1`] = ` \`\`\`sh yarn --version \`\`\`~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. Fork the repo and create your branch from \`master\`. +1. Fork the repo and create your branch from \`master\`. - Open terminal (e.g. Terminal, iTerm, Git Bash or Git Shell) and type: + Open terminal (e.g. Terminal, iTerm, Git Bash or Git Shell) and type: - \`\`\`sh - git clone https://github.com//jest - cd jest - git checkout -b my_branch - \`\`\` + \`\`\`sh + git clone https://github.com//jest + cd jest + git checkout -b my_branch + \`\`\` - Note: Replace \`\` with your GitHub username + Note: Replace \`\` with your GitHub username -2. Run \`yarn install\`. On Windows: To install - [Yarn](https://yarnpkg.com/en/docs/install#windows-tab) on Windows you may - need to download either node.js or Chocolatey
+2. Run \`yarn install\`. On Windows: To install + [Yarn](https://yarnpkg.com/en/docs/install#windows-tab) on Windows you may + need to download either node.js or Chocolatey
- \`\`\`sh - yarn install - \`\`\` + \`\`\`sh + yarn install + \`\`\` - To check your version of Yarn and ensure it's installed you can type: + To check your version of Yarn and ensure it's installed you can type: - \`\`\`sh - yarn --version - \`\`\` + \`\`\`sh + yarn --version + \`\`\` `; @@ -99,6 +147,16 @@ exports[`indent.md 1`] = ` Fenced Code Block Fenced Code Block \`\`\` + + + +1. Change to your home directory: + + cd + +2. List the contents: + + ls -l ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Indented Code Block Indented Code Block @@ -114,6 +172,16 @@ exports[`indent.md 1`] = ` Fenced Code Block \`\`\` + + +1. Change to your home directory: + + cd + +2. List the contents: + + ls -l + `; exports[`lang.md 1`] = ` diff --git a/tests/markdown_code/indent.md b/tests/markdown_code/indent.md index 146a43ba..7a1819ac 100644 --- a/tests/markdown_code/indent.md +++ b/tests/markdown_code/indent.md @@ -11,3 +11,13 @@ Fenced Code Block Fenced Code Block ``` + + + +1. Change to your home directory: + + cd + +2. List the contents: + + ls -l diff --git a/tests/markdown_footnoteDefinition/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_footnoteDefinition/__snapshots__/jsfmt.spec.js.snap index 81e68e08..92997113 100644 --- a/tests/markdown_footnoteDefinition/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_footnoteDefinition/__snapshots__/jsfmt.spec.js.snap @@ -3,7 +3,121 @@ exports[`long.md 1`] = ` [^hello]: this is a long long long long long long long long long long long long long paragraph. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -[^hello]: this is a long long long long long long long long long long long long long paragraph. +[^hello]: + + this is a long long long long long long long long long long long long long + paragraph. + +`; + +exports[`multiline.md 1`] = ` +[^fn1]: + + > \`\`\`rs + > fn main() { + > println!("this is some Rust!"); + > } + > \`\`\` + +[^fn2]: Here is a footnote which includes code. + + \`\`\`rs + fn main() { + println!("this is some Rust!"); + } + \`\`\` + +[^fn2]: Here is a footnote which includes code. Here is a footnote which includes code. Here is a footnote which includes code. + + \`\`\`rs + fn main() { + println!("this is some Rust!"); + } + \`\`\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +[^fn1]: + + > \`\`\`rs + > fn main() { + > println!("this is some Rust!"); + > } + > \`\`\` + +[^fn2]: Here is a footnote which includes code. + + \`\`\`rs + fn main() { + println!("this is some Rust!"); + } + \`\`\` + +[^fn2]: + + Here is a footnote which includes code. Here is a footnote which includes + code. Here is a footnote which includes code. + + \`\`\`rs + fn main() { + println!("this is some Rust!"); + } + \`\`\` + +`; + +exports[`sibling.md 1`] = ` +[^a]: a +[^a]: a +[^a]: a +[^a]: > 123 +[^a]: a +[^a]: > 123 +[^a]: a +[^a]: a +[^a]: a + +--- + +[^a]: a +[^a]: a +[^a]: a +[^a]: > 123\\ + > 456 +[^a]: a +[^a]: > 123\\ + > 456 +[^a]: a +[^a]: a +[^a]: a +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +[^a]: a +[^a]: a +[^a]: a +[^a]: > 123 +[^a]: a +[^a]: > 123 +[^a]: a +[^a]: a +[^a]: a + +--- + +[^a]: a +[^a]: a +[^a]: a +[^a]: + + > 123\\ + > 456 + +[^a]: a +[^a]: + + > 123\\ + > 456 + +[^a]: a +[^a]: a +[^a]: a `; diff --git a/tests/markdown_footnoteDefinition/multiline.md b/tests/markdown_footnoteDefinition/multiline.md new file mode 100644 index 00000000..3c3fd3dc --- /dev/null +++ b/tests/markdown_footnoteDefinition/multiline.md @@ -0,0 +1,23 @@ +[^fn1]: + + > ```rs + > fn main() { + > println!("this is some Rust!"); + > } + > ``` + +[^fn2]: Here is a footnote which includes code. + + ```rs + fn main() { + println!("this is some Rust!"); + } + ``` + +[^fn2]: Here is a footnote which includes code. Here is a footnote which includes code. Here is a footnote which includes code. + + ```rs + fn main() { + println!("this is some Rust!"); + } + ``` diff --git a/tests/markdown_footnoteDefinition/sibling.md b/tests/markdown_footnoteDefinition/sibling.md new file mode 100644 index 00000000..d6d38ad7 --- /dev/null +++ b/tests/markdown_footnoteDefinition/sibling.md @@ -0,0 +1,23 @@ +[^a]: a +[^a]: a +[^a]: a +[^a]: > 123 +[^a]: a +[^a]: > 123 +[^a]: a +[^a]: a +[^a]: a + +--- + +[^a]: a +[^a]: a +[^a]: a +[^a]: > 123\ + > 456 +[^a]: a +[^a]: > 123\ + > 456 +[^a]: a +[^a]: a +[^a]: a diff --git a/tests/markdown_html/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_html/__snapshots__/jsfmt.spec.js.snap index ab27b98f..9b031dee 100644 --- a/tests/markdown_html/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_html/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,56 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`multiline.md 1`] = ` +1. Some test text, the goal is to have the html table below nested within this number. When formating on save Prettier will continue to add an indent each time pushing the table further and further out of sync. + + + + + + + + + + + + + + + + + + + + +
TestTable
willbe
pushedWhen
Format onSave
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. Some test text, the goal is to have the html table below nested within this + number. When formating on save Prettier will continue to add an indent each + time pushing the table further and further out of sync. + + + + + + + + + + + + + + + + + + + + +
TestTable
willbe
pushedWhen
Format onSave
+ +`; + exports[`simple.md 1`] = ` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/markdown_html/multiline.md b/tests/markdown_html/multiline.md new file mode 100644 index 00000000..16716e4d --- /dev/null +++ b/tests/markdown_html/multiline.md @@ -0,0 +1,22 @@ +1. Some test text, the goal is to have the html table below nested within this number. When formating on save Prettier will continue to add an indent each time pushing the table further and further out of sync. + + + + + + + + + + + + + + + + + + + + +
TestTable
willbe
pushedWhen
Format onSave
diff --git a/tests/markdown_ignore/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_ignore/__snapshots__/jsfmt.spec.js.snap index 6f30d6c7..0ef30454 100644 --- a/tests/markdown_ignore/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_ignore/__snapshots__/jsfmt.spec.js.snap @@ -27,3 +27,52 @@ This is a long long long long long long long long long long long long long long This is a long long long long long long long long long long long long long long long paragraph. `; + +exports[`top-level-range.md 1`] = ` + + + +| some | table | +| - | - | +| 1 | a | +| 2 | b | + + + + +> +> +> +> | some | table | +> | - | - | +> | 1 | a | +> | 2 | b | +> +> +> +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + +| some | table | +| - | - | +| 1 | a | +| 2 | b | + + + + +> +> +> +> +> | some | table | +> | ---- | ----- | +> | 1 | a | +> | 2 | b | +> +> +> +> + +`; diff --git a/tests/markdown_ignore/top-level-range.md b/tests/markdown_ignore/top-level-range.md new file mode 100644 index 00000000..9773a4db --- /dev/null +++ b/tests/markdown_ignore/top-level-range.md @@ -0,0 +1,21 @@ + + + +| some | table | +| - | - | +| 1 | a | +| 2 | b | + + + + +> +> +> +> | some | table | +> | - | - | +> | 1 | a | +> | 2 | b | +> +> +> diff --git a/tests/markdown_imageReference/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_imageReference/__snapshots__/jsfmt.spec.js.snap index 399f3c8a..8dc23177 100644 --- a/tests/markdown_imageReference/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_imageReference/__snapshots__/jsfmt.spec.js.snap @@ -14,6 +14,13 @@ exports[`full.md 1`] = ` `; +exports[`full-no-alt.md 1`] = ` +![][world] +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +![][world] + +`; + exports[`shortcut.md 1`] = ` ![hello] ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/markdown_imageReference/full-no-alt.md b/tests/markdown_imageReference/full-no-alt.md new file mode 100644 index 00000000..140233b6 --- /dev/null +++ b/tests/markdown_imageReference/full-no-alt.md @@ -0,0 +1 @@ +![][world] diff --git a/tests/markdown_link/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_link/__snapshots__/jsfmt.spec.js.snap index 8b6633d7..5ac2feba 100644 --- a/tests/markdown_link/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_link/__snapshots__/jsfmt.spec.js.snap @@ -14,6 +14,20 @@ exports[`autolink.md 2`] = ` `; +exports[`entity.md 1`] = ` +[Test](http://localhost:8080/test?language=DE¤cy=EUR) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +[Test](http://localhost:8080/test?language=DE¤cy=EUR) + +`; + +exports[`entity.md 2`] = ` +[Test](http://localhost:8080/test?language=DE¤cy=EUR) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +[Test](http://localhost:8080/test?language=DE¤cy=EUR) + +`; + exports[`long.md 1`] = ` [podium context](https://github.schibsted.io/finn/podium/tree/master/packages/podium-context) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/tests/markdown_link/entity.md b/tests/markdown_link/entity.md new file mode 100644 index 00000000..009cede7 --- /dev/null +++ b/tests/markdown_link/entity.md @@ -0,0 +1 @@ +[Test](http://localhost:8080/test?language=DE¤cy=EUR) diff --git a/tests/markdown_list/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_list/__snapshots__/jsfmt.spec.js.snap index 5d48d30a..29789687 100644 --- a/tests/markdown_list/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_list/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,161 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`align.md 1`] = ` +1. 123 + +--- + +11. 123 + +--- + +111. 123 + +--- + +1111. 123 + +--- + +11111. 123 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. 123 + +--- + +11. 123 + +--- + +111. 123 + +--- + +1111. 123 + +--- + +11111. 123 + +`; + +exports[`align.md 2`] = ` +1. 123 + +--- + +11. 123 + +--- + +111. 123 + +--- + +1111. 123 + +--- + +11111. 123 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. 123 + +--- + +11. 123 + +--- + +111. 123 + +--- + +1111. 123 + +--- + +11111. 123 + +`; + +exports[`align.md 3`] = ` +1. 123 + +--- + +11. 123 + +--- + +111. 123 + +--- + +1111. 123 + +--- + +11111. 123 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. 123 + +--- + +11. 123 + +--- + +111. 123 + +--- + +1111. 123 + +--- + +11111. 123 + +`; + +exports[`align.md 4`] = ` +1. 123 + +--- + +11. 123 + +--- + +111. 123 + +--- + +1111. 123 + +--- + +11111. 123 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. 123 + +--- + +11. 123 + +--- + +111. 123 + +--- + +1111. 123 + +--- + +11111. 123 + +`; + exports[`checkbox.md 1`] = ` - [ ] this is a long long long long long long long long long long long long long long paragraph. - [x] this is a long long long long long long long long long long long long long long paragraph. @@ -11,17 +167,995 @@ exports[`checkbox.md 1`] = ` `; +exports[`checkbox.md 2`] = ` +- [ ] this is a long long long long long long long long long long long long long long paragraph. +- [x] this is a long long long long long long long long long long long long long long paragraph. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* [ ] this is a long long long long long long long long long long long long + long long paragraph. +* [x] this is a long long long long long long long long long long long long + long long paragraph. + +`; + +exports[`checkbox.md 3`] = ` +- [ ] this is a long long long long long long long long long long long long long long paragraph. +- [x] this is a long long long long long long long long long long long long long long paragraph. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* [ ] this is a long long long long long long long long long long long long long + long paragraph. +* [x] this is a long long long long long long long long long long long long long + long paragraph. + +`; + +exports[`checkbox.md 4`] = ` +- [ ] this is a long long long long long long long long long long long long long long paragraph. +- [x] this is a long long long long long long long long long long long long long long paragraph. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* [ ] this is a long long long long long long long long long long long long long + long paragraph. +* [x] this is a long long long long long long long long long long long long long + long paragraph. + +`; + exports[`git-diff-friendly.md 1`] = ` 5. abc 1. def 999. ghi ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +5. abc +1. def +1. ghi + +`; + +exports[`git-diff-friendly.md 2`] = ` +5. abc +1. def +999. ghi +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +5. abc +1. def +1. ghi + +`; + +exports[`git-diff-friendly.md 3`] = ` +5. abc +1. def +999. ghi +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5. abc 1. def 1. ghi `; +exports[`git-diff-friendly.md 4`] = ` +5. abc +1. def +999. ghi +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +5. abc +1. def +1. ghi + +`; + +exports[`indent.md 1`] = ` +- [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +12345678) [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + +1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + +12345678) [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + +`; + +exports[`indent.md 2`] = ` +- [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +12345678) [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + +1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + +12345678) [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + +`; + +exports[`indent.md 3`] = ` +- [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +12345678) [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + +1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + +12345678) [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a a + +`; + +exports[`indent.md 4`] = ` +- [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +12345678) [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + +1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a + +12345678) [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + * a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + * [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + a a a a a a a a a a a a a a a + +`; + +exports[`interrupt.md 1`] = ` +* Something +### Some heading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Something + +### Some heading + +`; + +exports[`interrupt.md 2`] = ` +* Something +### Some heading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Something + +### Some heading + +`; + +exports[`interrupt.md 3`] = ` +* Something +### Some heading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Something + +### Some heading + +`; + +exports[`interrupt.md 4`] = ` +* Something +### Some heading +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Something + +### Some heading + +`; + exports[`long-paragraph.md 1`] = ` - This is a long long long long long long long long long long long long long long paragraph. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -30,6 +1164,30 @@ exports[`long-paragraph.md 1`] = ` `; +exports[`long-paragraph.md 2`] = ` +- This is a long long long long long long long long long long long long long long paragraph. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* This is a long long long long long long long long long long long long long + long paragraph. + +`; + +exports[`long-paragraph.md 3`] = ` +- This is a long long long long long long long long long long long long long long paragraph. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* This is a long long long long long long long long long long long long long + long paragraph. + +`; + +exports[`long-paragraph.md 4`] = ` +- This is a long long long long long long long long long long long long long long paragraph. +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* This is a long long long long long long long long long long long long long + long paragraph. + +`; + exports[`loose.md 1`] = ` - 123 @@ -57,6 +1215,87 @@ exports[`loose.md 1`] = ` `; +exports[`loose.md 2`] = ` +- 123 + + - abc + +- 456 + + - def + +- 789 + + - ghi +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 + + * abc + +* 456 + + * def + +* 789 + + * ghi + +`; + +exports[`loose.md 3`] = ` +- 123 + + - abc + +- 456 + + - def + +- 789 + + - ghi +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 + + * abc + +* 456 + + * def + +* 789 + + * ghi + +`; + +exports[`loose.md 4`] = ` +- 123 + + - abc + +- 456 + + - def + +- 789 + + - ghi +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 + + * abc + +* 456 + + * def + +* 789 + + * ghi + +`; + exports[`multiline.md 1`] = ` - 123 456 @@ -66,6 +1305,33 @@ exports[`multiline.md 1`] = ` `; +exports[`multiline.md 2`] = ` +- 123 + 456 + 789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 456 789 + +`; + +exports[`multiline.md 3`] = ` +- 123 + 456 + 789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 456 789 + +`; + +exports[`multiline.md 4`] = ` +- 123 + 456 + 789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 456 789 + +`; + exports[`nested.md 1`] = ` - Level 1 - Level 2 @@ -77,6 +1343,39 @@ exports[`nested.md 1`] = ` `; +exports[`nested.md 2`] = ` +- Level 1 + - Level 2 + - Level 3 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Level 1 + * Level 2 + * Level 3 + +`; + +exports[`nested.md 3`] = ` +- Level 1 + - Level 2 + - Level 3 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Level 1 + * Level 2 + * Level 3 + +`; + +exports[`nested.md 4`] = ` +- Level 1 + - Level 2 + - Level 3 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* Level 1 + * Level 2 + * Level 3 + +`; + exports[`nested-checkbox.md 1`] = ` * parent list item parent list item parent list item parent list item parent list item parent list item @@ -110,11 +1409,143 @@ exports[`nested-checkbox.md 1`] = ` `; +exports[`nested-checkbox.md 2`] = ` +* parent list item parent list item parent list item parent list item parent list item parent list item + + * child list item child list item child list item child list item child list item child list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph + +* [x] parent task list item parent task list item parent task list item parent task list item + + * [x] child task list item child task list item child task list item child task list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* parent list item parent list item parent list item parent list item parent + list item parent list item + + * child list item child list item child list item child list item child + list item child list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph + paragraph paragraph + +* [x] parent task list item parent task list item parent task list item parent + task list item + + * [x] child task list item child task list item child task list item child + task list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph + paragraph paragraph + +`; + +exports[`nested-checkbox.md 3`] = ` +* parent list item parent list item parent list item parent list item parent list item parent list item + + * child list item child list item child list item child list item child list item child list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph + +* [x] parent task list item parent task list item parent task list item parent task list item + + * [x] child task list item child task list item child task list item child task list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* parent list item parent list item parent list item parent list item parent + list item parent list item + + * child list item child list item child list item child list item child + list item child list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph + paragraph paragraph + +* [x] parent task list item parent task list item parent task list item parent + task list item + + * [x] child task list item child task list item child task list item child + task list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph + paragraph paragraph + +`; + +exports[`nested-checkbox.md 4`] = ` +* parent list item parent list item parent list item parent list item parent list item parent list item + + * child list item child list item child list item child list item child list item child list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph + +* [x] parent task list item parent task list item parent task list item parent task list item + + * [x] child task list item child task list item child task list item child task list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph paragraph +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* parent list item parent list item parent list item parent list item parent + list item parent list item + + * child list item child list item child list item child list item child list + item child list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph + paragraph paragraph + +* [x] parent task list item parent task list item parent task list item parent + task list item + + * [x] child task list item child task list item child task list item child + task list item + + paragraph paragraph paragraph paragraph paragraph paragraph paragraph + paragraph paragraph + +`; + exports[`ordered.md 1`] = ` 1. 123 1. 456 1. 789 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. 123 +1. 456 +1. 789 + +`; + +exports[`ordered.md 2`] = ` +1. 123 +1. 456 +1. 789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. 123 +1. 456 +1. 789 + +`; + +exports[`ordered.md 3`] = ` +1. 123 +1. 456 +1. 789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +1. 123 +1. 456 +1. 789 + +`; + +exports[`ordered.md 4`] = ` +1. 123 +1. 456 +1. 789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. 123 1. 456 1. 789 @@ -140,6 +1571,63 @@ exports[`separate.md 1`] = ` `; +exports[`separate.md 2`] = ` +- 123 +- 123 +- 123 + +* 123 +* 123 +* 123 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 +* 123 +* 123 + +- 123 +- 123 +- 123 + +`; + +exports[`separate.md 3`] = ` +- 123 +- 123 +- 123 + +* 123 +* 123 +* 123 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 +* 123 +* 123 + +- 123 +- 123 +- 123 + +`; + +exports[`separate.md 4`] = ` +- 123 +- 123 +- 123 + +* 123 +* 123 +* 123 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 +* 123 +* 123 + +- 123 +- 123 +- 123 + +`; + exports[`simple.md 1`] = ` - 123 - 456 @@ -151,11 +1639,77 @@ exports[`simple.md 1`] = ` `; +exports[`simple.md 2`] = ` +- 123 +- 456 +- 789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 +* 456 +* 789 + +`; + +exports[`simple.md 3`] = ` +- 123 +- 456 +- 789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 +* 456 +* 789 + +`; + +exports[`simple.md 4`] = ` +- 123 +- 456 +- 789 +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +* 123 +* 456 +* 789 + +`; + exports[`start.md 1`] = ` 5. abc 6. def 7. ghi ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +5. abc +6. def +7. ghi + +`; + +exports[`start.md 2`] = ` +5. abc +6. def +7. ghi +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +5. abc +6. def +7. ghi + +`; + +exports[`start.md 3`] = ` +5. abc +6. def +7. ghi +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +5. abc +6. def +7. ghi + +`; + +exports[`start.md 4`] = ` +5. abc +6. def +7. ghi +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 5. abc 6. def 7. ghi diff --git a/tests/markdown_list/align.md b/tests/markdown_list/align.md new file mode 100644 index 00000000..2b799ddd --- /dev/null +++ b/tests/markdown_list/align.md @@ -0,0 +1,17 @@ +1. 123 + +--- + +11. 123 + +--- + +111. 123 + +--- + +1111. 123 + +--- + +11111. 123 diff --git a/tests/markdown_list/indent.md b/tests/markdown_list/indent.md new file mode 100644 index 00000000..caef8c1a --- /dev/null +++ b/tests/markdown_list/indent.md @@ -0,0 +1,85 @@ +- [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + +12345678) [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + - [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 1. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678) a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + 12345678. [ ] a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a + + b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b b + + a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a a diff --git a/tests/markdown_list/interrupt.md b/tests/markdown_list/interrupt.md new file mode 100644 index 00000000..a24c9ff8 --- /dev/null +++ b/tests/markdown_list/interrupt.md @@ -0,0 +1,2 @@ +* Something +### Some heading diff --git a/tests/markdown_list/jsfmt.spec.js b/tests/markdown_list/jsfmt.spec.js index b62fc584..26ddb7f7 100644 --- a/tests/markdown_list/jsfmt.spec.js +++ b/tests/markdown_list/jsfmt.spec.js @@ -1 +1,4 @@ run_spec(__dirname, ["markdown"], { proseWrap: "always" }); +run_spec(__dirname, ["markdown"], { proseWrap: "always", tabWidth: 4 }); +run_spec(__dirname, ["markdown"], { proseWrap: "always", tabWidth: 999 }); +run_spec(__dirname, ["markdown"], { proseWrap: "always", tabWidth: 0 }); diff --git a/tests/markdown_spec/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_spec/__snapshots__/jsfmt.spec.js.snap index a81a1cab..4a674fc8 100644 --- a/tests/markdown_spec/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_spec/__snapshots__/jsfmt.spec.js.snap @@ -671,7 +671,6 @@ exports[`example-66.md 1`] = ` --- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- - --- `; @@ -794,9 +793,9 @@ exports[`example-77.md 1`] = ` - bar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. foo +1. foo - * bar + * bar `; @@ -1540,7 +1539,7 @@ exports[`example-138.md 1`] = ` bar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ >
-foo +> foo bar @@ -2420,11 +2419,11 @@ exports[`example-214.md 1`] = ` > A block quote. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. A paragraph with two lines. +1. A paragraph with two lines. - indented code + indented code - > A block quote. + > A block quote. `; @@ -2477,9 +2476,9 @@ exports[`example-219.md 1`] = ` >> >> two ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> > 1. one +> > 1. one > > -> > two +> > two `; @@ -2569,15 +2568,15 @@ exports[`example-223.md 1`] = ` > bam ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. foo +1. foo - \`\`\` - bar - \`\`\` + \`\`\` + bar + \`\`\` - baz + baz - > bam + > bam `; @@ -2616,7 +2615,7 @@ exports[`example-225.md 1`] = ` exports[`example-226.md 1`] = ` 123456789. ok ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -123456789. ok +123456789. ok `; @@ -2630,14 +2629,14 @@ exports[`example-227.md 1`] = ` exports[`example-228.md 1`] = ` 0. ok ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -0. ok +0. ok `; exports[`example-229.md 1`] = ` 003. ok ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -3. ok +3. ok `; @@ -2692,11 +2691,11 @@ exports[`example-234.md 1`] = ` more code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. indented code +1. indented code - paragraph + paragraph - more code + more code `; @@ -2707,11 +2706,11 @@ exports[`example-235.md 1`] = ` more code ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. indented code +1. indented code - paragraph + paragraph - more code + more code `; @@ -2786,9 +2785,9 @@ exports[`example-243.md 1`] = ` 2. 3. bar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. foo +1. foo 2. -3. bar +3. bar `; @@ -2807,11 +2806,11 @@ exports[`example-245.md 1`] = ` > A block quote. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. A paragraph with two lines. +1. A paragraph with two lines. - indented code + indented code - > A block quote. + > A block quote. `; @@ -2823,11 +2822,11 @@ exports[`example-246.md 1`] = ` > A block quote. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. A paragraph with two lines. +1. A paragraph with two lines. - indented code + indented code - > A block quote. + > A block quote. `; @@ -2839,11 +2838,11 @@ exports[`example-247.md 1`] = ` > A block quote. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. A paragraph with two lines. +1. A paragraph with two lines. - indented code + indented code - > A block quote. + > A block quote. `; @@ -2872,11 +2871,11 @@ with two lines. > A block quote. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. A paragraph with two lines. +1. A paragraph with two lines. - indented code + indented code - > A block quote. + > A block quote. `; @@ -2884,7 +2883,7 @@ exports[`example-250.md 1`] = ` 1. A paragraph with two lines. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. A paragraph with two lines. +1. A paragraph with two lines. `; @@ -2892,7 +2891,7 @@ exports[`example-251.md 1`] = ` > 1. > Blockquote continued here. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> 1. > Blockquote continued here. +> 1. > Blockquote continued here. `; @@ -2900,7 +2899,7 @@ exports[`example-252.md 1`] = ` > 1. > Blockquote > continued here. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -> 1. > Blockquote continued here. +> 1. > Blockquote continued here. `; @@ -2955,7 +2954,7 @@ exports[`example-257.md 1`] = ` exports[`example-258.md 1`] = ` 1. - 2. foo ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. * 2. foo +1. * 2. foo `; @@ -2988,10 +2987,10 @@ exports[`example-261.md 1`] = ` 2. bar 3) baz ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. foo -2. bar +1. foo +2. bar -3) baz +3) baz `; @@ -3252,11 +3251,11 @@ exports[`example-281.md 1`] = ` bar ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -1. \`\`\` - foo - \`\`\` +1. \`\`\` + foo + \`\`\` - bar + bar `; diff --git a/tests/markdown_toml/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_toml/__snapshots__/jsfmt.spec.js.snap index 868297fc..eb1f524d 100644 --- a/tests/markdown_toml/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_toml/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`empty.md 1`] = ` ++++ ++++ +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ++++ ++++ + +`; + exports[`simple.md 1`] = ` +++ date: '2017-10-10T22:49:47.369Z' diff --git a/tests/markdown_toml/empty.md b/tests/markdown_toml/empty.md new file mode 100644 index 00000000..ac36e062 --- /dev/null +++ b/tests/markdown_toml/empty.md @@ -0,0 +1,2 @@ ++++ ++++ diff --git a/tests/markdown_yaml/__snapshots__/jsfmt.spec.js.snap b/tests/markdown_yaml/__snapshots__/jsfmt.spec.js.snap index ca4455bf..5058697a 100644 --- a/tests/markdown_yaml/__snapshots__/jsfmt.spec.js.snap +++ b/tests/markdown_yaml/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,14 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`empty.md 1`] = ` +--- +--- +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +--- + +`; + exports[`simple.md 1`] = ` --- hello: world diff --git a/tests/markdown_yaml/empty.md b/tests/markdown_yaml/empty.md new file mode 100644 index 00000000..a845151c --- /dev/null +++ b/tests/markdown_yaml/empty.md @@ -0,0 +1,2 @@ +--- +--- diff --git a/tests/multiparser_js_css/__snapshots__/jsfmt.spec.js.snap b/tests/multiparser_js_css/__snapshots__/jsfmt.spec.js.snap index 33079547..3a93d54f 100644 --- a/tests/multiparser_js_css/__snapshots__/jsfmt.spec.js.snap +++ b/tests/multiparser_js_css/__snapshots__/jsfmt.spec.js.snap @@ -30,6 +30,42 @@ border : rebeccapurple\`; styled(ExistingComponent).attr({})\` border : rebeccapurple\`; + +styled.div\` + color: \${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + \${props => props.small ? 'font-size: 0.8em;' : ''}; +\` + +styled.div\` + color: \${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + \${props => props.small ? 'font-size: 0.8em;' : ''} +\` + +styled.div\` + /* prettier-ignore */ + color: \${props => props.theme.colors.paragraph}; + \${props => props.small ? 'font-size: 0.8em;' : ''}; +\` + +styled.div\` + color: \${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + \${props => props.small ? 'font-size: 0.8em;' : ''}; + /* prettier-ignore */ + \${props => props.red ? 'color: red;' : ''}; +\` + +styled.div\` + /* prettier-ignore */ + color: \${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + \${props => props.small ? 'font-size: 0.8em;' : ''}; + /* prettier-ignore */ + \${props => props.red ? 'color: red;' : ''}; + /* prettier-ignore */ +\` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ const ListItem = styled.li\`\`; @@ -64,4 +100,40 @@ styled(ExistingComponent).attr({})\` border: rebeccapurple; \`; +styled.div\` + color: \${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + \${props => (props.small ? "font-size: 0.8em;" : "")}; +\`; + +styled.div\` + color: \${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + \${props => (props.small ? "font-size: 0.8em;" : "")} +\`; + +styled.div\` + /* prettier-ignore */ + color: \${props => props.theme.colors.paragraph}; + \${props => (props.small ? "font-size: 0.8em;" : "")}; +\`; + +styled.div\` + color: \${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + \${props => (props.small ? "font-size: 0.8em;" : "")}; + /* prettier-ignore */ + \${props => (props.red ? "color: red;" : "")}; +\`; + +styled.div\` + /* prettier-ignore */ + color: \${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + \${props => (props.small ? "font-size: 0.8em;" : "")}; + /* prettier-ignore */ + \${props => (props.red ? "color: red;" : "")}; + /* prettier-ignore */ +\`; + `; diff --git a/tests/multiparser_js_css/styled-components.js b/tests/multiparser_js_css/styled-components.js index 08372911..ddd68baa 100644 --- a/tests/multiparser_js_css/styled-components.js +++ b/tests/multiparser_js_css/styled-components.js @@ -27,3 +27,39 @@ border : rebeccapurple`; styled(ExistingComponent).attr({})` border : rebeccapurple`; + +styled.div` + color: ${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + ${props => props.small ? 'font-size: 0.8em;' : ''}; +` + +styled.div` + color: ${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + ${props => props.small ? 'font-size: 0.8em;' : ''} +` + +styled.div` + /* prettier-ignore */ + color: ${props => props.theme.colors.paragraph}; + ${props => props.small ? 'font-size: 0.8em;' : ''}; +` + +styled.div` + color: ${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + ${props => props.small ? 'font-size: 0.8em;' : ''}; + /* prettier-ignore */ + ${props => props.red ? 'color: red;' : ''}; +` + +styled.div` + /* prettier-ignore */ + color: ${props => props.theme.colors.paragraph}; + /* prettier-ignore */ + ${props => props.small ? 'font-size: 0.8em;' : ''}; + /* prettier-ignore */ + ${props => props.red ? 'color: red;' : ''}; + /* prettier-ignore */ +` diff --git a/tests/multiparser_js_graphql/__snapshots__/jsfmt.spec.js.snap b/tests/multiparser_js_graphql/__snapshots__/jsfmt.spec.js.snap index 580b80d4..4a1ccc37 100644 --- a/tests/multiparser_js_graphql/__snapshots__/jsfmt.spec.js.snap +++ b/tests/multiparser_js_graphql/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,22 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`backticks.js 1`] = ` +gql\` + "\\\`foo\\\` mutation payload." + type FooPayload { + bar: String + } +\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +gql\` + "\\\`foo\\\` mutation payload." + type FooPayload { + bar: String + } +\`; + +`; + exports[`expressions.js 1`] = ` graphql(schema, \` query allPartsByManufacturerName($name: String!) { @@ -392,6 +409,55 @@ gql\` `; +exports[`invalid.js 1`] = ` +// none of the embedded GraphQL should be formatted +// for they have an invalid escape sequence + +gql\` + "\\x" + type Foo { + a: string + } +\`; + +gql\` + type Foo { + a: string + } + + \${stuff} + + "\\x" + type Bar { + b : string + } +\`; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +// none of the embedded GraphQL should be formatted +// for they have an invalid escape sequence + +gql\` + "\\x" + type Foo { + a: string + } +\`; + +gql\` + type Foo { + a: string + } + + \${stuff} + + "\\x" + type Bar { + b : string + } +\`; + +`; + exports[`react-relay.js 1`] = ` const { graphql } = require("react-relay"); diff --git a/tests/multiparser_js_graphql/backticks.js b/tests/multiparser_js_graphql/backticks.js new file mode 100644 index 00000000..0ffaa37b --- /dev/null +++ b/tests/multiparser_js_graphql/backticks.js @@ -0,0 +1,6 @@ +gql` + "\`foo\` mutation payload." + type FooPayload { + bar: String + } +` diff --git a/tests/multiparser_js_graphql/invalid.js b/tests/multiparser_js_graphql/invalid.js new file mode 100644 index 00000000..df6cfdea --- /dev/null +++ b/tests/multiparser_js_graphql/invalid.js @@ -0,0 +1,22 @@ +// none of the embedded GraphQL should be formatted +// for they have an invalid escape sequence + +gql` + "\x" + type Foo { + a: string + } +`; + +gql` + type Foo { + a: string + } + + ${stuff} + + "\x" + type Bar { + b : string + } +`; diff --git a/tests/multiparser_js_markdown/0-indent.js b/tests/multiparser_js_markdown/0-indent.js new file mode 100644 index 00000000..6968d9ec --- /dev/null +++ b/tests/multiparser_js_markdown/0-indent.js @@ -0,0 +1,19 @@ +md` +This line shouldn't be indented at all in the resulting output. +` + +if (true) { + md` +text1 +- 123 + - 456 + +text2 +- 123 + - 456 + +text3 +- 123 + - 456 +`; +} diff --git a/tests/multiparser_js_markdown/__snapshots__/jsfmt.spec.js.snap b/tests/multiparser_js_markdown/__snapshots__/jsfmt.spec.js.snap index 53246b1c..490cbda4 100644 --- a/tests/multiparser_js_markdown/__snapshots__/jsfmt.spec.js.snap +++ b/tests/multiparser_js_markdown/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,51 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`0-indent.js 1`] = ` +md\` +This line shouldn't be indented at all in the resulting output. +\` + +if (true) { + md\` +text1 +- 123 + - 456 + +text2 +- 123 + - 456 + +text3 +- 123 + - 456 +\`; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +md\` +This line shouldn't be indented at all in the resulting output. +\`; + +if (true) { + md\` +text1 + +* 123 + * 456 + +text2 + +* 123 + * 456 + +text3 + +* 123 + * 456 + \`; +} + +`; + exports[`codeblock.js 1`] = ` md\` \\\`\\\`\\\`js @@ -12,13 +58,13 @@ markdown\\\` \`; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ md\` +~~~js +markdown\\\` ~~~js - markdown\\\` - ~~~js - console.log("hi"); - ~~~ - \\\`; + console.log("hi"); ~~~ +\\\`; +~~~ \`; `; @@ -80,7 +126,7 @@ exports[`single-line.js 1`] = ` markdown\`# hello\` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ markdown\` - # hello +# hello \`; `; diff --git a/tests/multiparser_markdown_css/__snapshots__/jsfmt.spec.js.snap b/tests/multiparser_markdown_css/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..65c2b60b --- /dev/null +++ b/tests/multiparser_markdown_css/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,54 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`css-comment.md 1`] = ` +\`\`\`pcss +.Avatar { + /* ... */ + + &__image { + /* ... */ + + @container (width > 100px) { + /* + Change some styles on the image element when the container is + wider than 100px + */ + } + } + + @container (aspect-ratio > 3) { + /* Change styles on the avatar itself, when the aspect-ratio is grater than 3 */ + } + + @container (width > 100px) and (height > 100px) { + /* ... */ + } +} +\`\`\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\`\`\`pcss +.Avatar { + /* ... */ + + &__image { + /* ... */ + + @container (width > 100px) { + /* + Change some styles on the image element when the container is + wider than 100px + */ + } + } + + @container (aspect-ratio > 3) { + /* Change styles on the avatar itself, when the aspect-ratio is grater than 3 */ + } + + @container (width > 100px) and (height > 100px) { + /* ... */ + } +} +\`\`\` + +`; diff --git a/tests/multiparser_markdown_css/css-comment.md b/tests/multiparser_markdown_css/css-comment.md new file mode 100644 index 00000000..59a3168a --- /dev/null +++ b/tests/multiparser_markdown_css/css-comment.md @@ -0,0 +1,24 @@ +```pcss +.Avatar { + /* ... */ + + &__image { + /* ... */ + + @container (width > 100px) { + /* + Change some styles on the image element when the container is + wider than 100px + */ + } + } + + @container (aspect-ratio > 3) { + /* Change styles on the avatar itself, when the aspect-ratio is grater than 3 */ + } + + @container (width > 100px) and (height > 100px) { + /* ... */ + } +} +``` diff --git a/tests/multiparser_markdown_css/jsfmt.spec.js b/tests/multiparser_markdown_css/jsfmt.spec.js new file mode 100644 index 00000000..ff3b58b0 --- /dev/null +++ b/tests/multiparser_markdown_css/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["markdown"]); diff --git a/tests/multiparser_markdown_js/__snapshots__/jsfmt.spec.js.snap b/tests/multiparser_markdown_js/__snapshots__/jsfmt.spec.js.snap index 4c94e48e..d53c85a9 100644 --- a/tests/multiparser_markdown_js/__snapshots__/jsfmt.spec.js.snap +++ b/tests/multiparser_markdown_js/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,61 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`jsx-comment.md 1`] = ` +\`\`\`jsx +const Foo = () => { + return ( +
+ {/* + This links to a page that does not yet exist. + */} +
+
+ ); +}; +\`\`\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +\`\`\`jsx +const Foo = () => { + return ( +
+ {/* + This links to a page that does not yet exist. + */} +
+
+ ); +}; +\`\`\` + +`; + +exports[`markdown-preview-enhanced.md 1`] = ` +## plain js block + +\`\`\`js +console.log( "hello world" ); +\`\`\` + +## js block with arguments + +\`\`\`js {cmd=node .line-numbers} +console.log( "hello world" ); +\`\`\` +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +## plain js block + +\`\`\`js +console.log("hello world"); +\`\`\` + +## js block with arguments + +\`\`\`js {cmd=node .line-numbers} +console.log("hello world"); +\`\`\` + +`; + exports[`trailing-comma.md 1`] = ` ### Some heading diff --git a/tests/multiparser_markdown_js/jsx-comment.md b/tests/multiparser_markdown_js/jsx-comment.md new file mode 100644 index 00000000..9db8944d --- /dev/null +++ b/tests/multiparser_markdown_js/jsx-comment.md @@ -0,0 +1,12 @@ +```jsx +const Foo = () => { + return ( +
+ {/* + This links to a page that does not yet exist. + */} +
+
+ ); +}; +``` diff --git a/tests/multiparser_markdown_js/markdown-preview-enhanced.md b/tests/multiparser_markdown_js/markdown-preview-enhanced.md new file mode 100644 index 00000000..34f8f360 --- /dev/null +++ b/tests/multiparser_markdown_js/markdown-preview-enhanced.md @@ -0,0 +1,11 @@ +## plain js block + +```js +console.log( "hello world" ); +``` + +## js block with arguments + +```js {cmd=node .line-numbers} +console.log( "hello world" ); +``` diff --git a/tests/multiparser_vue/__snapshots__/jsfmt.spec.js.snap b/tests/multiparser_vue/__snapshots__/jsfmt.spec.js.snap index 938d84cc..bdb5d7c0 100644 --- a/tests/multiparser_vue/__snapshots__/jsfmt.spec.js.snap +++ b/tests/multiparser_vue/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,58 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`lang-ts.vue 1`] = ` + + + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +`; + +exports[`lang-tsx.vue 1`] = ` + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +`; + exports[`template-bind.vue 1`] = ` + `; exports[`template-class.vue 1`] = ` @@ -37,6 +91,7 @@ exports[`template-class.vue 1`] = ` > + `; exports[`vue-component.vue 1`] = ` @@ -54,6 +109,11 @@ module . exports = + + @@ -79,4 +139,12 @@ p { text-align: center; } + + + `; diff --git a/tests/multiparser_vue/lang-ts.vue b/tests/multiparser_vue/lang-ts.vue new file mode 100644 index 00000000..c9db0056 --- /dev/null +++ b/tests/multiparser_vue/lang-ts.vue @@ -0,0 +1,10 @@ + + + + diff --git a/tests/multiparser_vue/lang-tsx.vue b/tests/multiparser_vue/lang-tsx.vue new file mode 100644 index 00000000..665cd968 --- /dev/null +++ b/tests/multiparser_vue/lang-tsx.vue @@ -0,0 +1,7 @@ + diff --git a/tests/multiparser_vue/vue-component.vue b/tests/multiparser_vue/vue-component.vue index 1f876584..a404d5be 100644 --- a/tests/multiparser_vue/vue-component.vue +++ b/tests/multiparser_vue/vue-component.vue @@ -15,3 +15,8 @@ module . exports = p { font-size : 2em ; text-align : center ; } + + diff --git a/tests/require-amd/__snapshots__/jsfmt.spec.js.snap b/tests/require-amd/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..9bb5f232 --- /dev/null +++ b/tests/require-amd/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,82 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`require.js 1`] = ` +require( + [ + 'jquery', + 'common/global.context', + 'common/log.event', + 'some_project/square', + 'some_project/rectangle', + 'some_project/triangle', + 'some_project/circle', + 'some_project/star', + ], + function($, Context, EventLogger, Square, Rectangle, Triangle, Circle, Star) { + + console.log('some code') + } +); + +define( + [ + 'jquery', + 'common/global.context', + 'common/log.event', + 'some_project/square', + 'some_project/rectangle', + 'some_project/triangle', + 'some_project/circle', + 'some_project/star', + ], + function($, Context, EventLogger, Square, Rectangle, Triangle, Circle, Star) { + + console.log('some code') + } +); +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +require([ + "jquery", + "common/global.context", + "common/log.event", + "some_project/square", + "some_project/rectangle", + "some_project/triangle", + "some_project/circle", + "some_project/star" +], function( + $, + Context, + EventLogger, + Square, + Rectangle, + Triangle, + Circle, + Star +) { + console.log("some code"); +}); + +define([ + "jquery", + "common/global.context", + "common/log.event", + "some_project/square", + "some_project/rectangle", + "some_project/triangle", + "some_project/circle", + "some_project/star" +], function( + $, + Context, + EventLogger, + Square, + Rectangle, + Triangle, + Circle, + Star +) { + console.log("some code"); +}); + +`; diff --git a/tests/require-amd/jsfmt.spec.js b/tests/require-amd/jsfmt.spec.js new file mode 100644 index 00000000..a17d3649 --- /dev/null +++ b/tests/require-amd/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["flow", "typescript", "babylon"]); diff --git a/tests/require-amd/require.js b/tests/require-amd/require.js new file mode 100644 index 00000000..6aefc026 --- /dev/null +++ b/tests/require-amd/require.js @@ -0,0 +1,33 @@ +require( + [ + 'jquery', + 'common/global.context', + 'common/log.event', + 'some_project/square', + 'some_project/rectangle', + 'some_project/triangle', + 'some_project/circle', + 'some_project/star', + ], + function($, Context, EventLogger, Square, Rectangle, Triangle, Circle, Star) { + + console.log('some code') + } +); + +define( + [ + 'jquery', + 'common/global.context', + 'common/log.event', + 'some_project/square', + 'some_project/rectangle', + 'some_project/triangle', + 'some_project/circle', + 'some_project/star', + ], + function($, Context, EventLogger, Square, Rectangle, Triangle, Circle, Star) { + + console.log('some code') + } +); diff --git a/tests/require-pragma/__snapshots__/jsfmt.spec.js.snap b/tests/require-pragma/js/__snapshots__/jsfmt.spec.js.snap similarity index 100% rename from tests/require-pragma/__snapshots__/jsfmt.spec.js.snap rename to tests/require-pragma/js/__snapshots__/jsfmt.spec.js.snap diff --git a/tests/require-pragma/js/jsfmt.spec.js b/tests/require-pragma/js/jsfmt.spec.js new file mode 100644 index 00000000..9351e117 --- /dev/null +++ b/tests/require-pragma/js/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["flow", "babylon", "typescript"], { requirePragma: true }); diff --git a/tests/require-pragma/module-with-pragma.js b/tests/require-pragma/js/module-with-pragma.js similarity index 100% rename from tests/require-pragma/module-with-pragma.js rename to tests/require-pragma/js/module-with-pragma.js diff --git a/tests/require-pragma/module-without-pragma.js b/tests/require-pragma/js/module-without-pragma.js similarity index 100% rename from tests/require-pragma/module-without-pragma.js rename to tests/require-pragma/js/module-without-pragma.js diff --git a/tests/require-pragma/jsfmt.spec.js b/tests/require-pragma/jsfmt.spec.js deleted file mode 100644 index fdccdebd..00000000 --- a/tests/require-pragma/jsfmt.spec.js +++ /dev/null @@ -1 +0,0 @@ -run_spec(__dirname, ["flow"], { requirePragma: true }); diff --git a/tests/require-pragma/markdown/__snapshots__/jsfmt.spec.js.snap b/tests/require-pragma/markdown/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..ed3cbb17 --- /dev/null +++ b/tests/require-pragma/markdown/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,106 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`empty-front-matter-with-pragma.md 1`] = ` +--- +--- + + + +I should be formatted !! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +--- + + + +I should be formatted !! + +`; + +exports[`empty-front-matter-without-pragma.md 1`] = ` +--- +--- + +I should stay as-is !! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +--- + +I should stay as-is !! + +`; + +exports[`front-matter-with-pragma.md 1`] = ` +--- +something +--- + + + +I should be formatted !! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +something +--- + + + +I should be formatted !! + +`; + +exports[`front-matter-without-pragma.md 1`] = ` +--- +something +--- + +I should stay as-is !! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +--- +something +--- + +I should stay as-is !! + +`; + +exports[`with-pragma.md 1`] = ` + + +I should be formatted !! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +I should be formatted !! + +`; + +exports[`with-pragma-in-multiline.md 1`] = ` + + +I should be formatted !! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + +I should be formatted !! + +`; + +exports[`without-pragma.md 1`] = ` +I should stay as-is !! +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +I should stay as-is !! + +`; diff --git a/tests/require-pragma/markdown/empty-front-matter-with-pragma.md b/tests/require-pragma/markdown/empty-front-matter-with-pragma.md new file mode 100644 index 00000000..080eaed2 --- /dev/null +++ b/tests/require-pragma/markdown/empty-front-matter-with-pragma.md @@ -0,0 +1,6 @@ +--- +--- + + + +I should be formatted !! diff --git a/tests/require-pragma/markdown/empty-front-matter-without-pragma.md b/tests/require-pragma/markdown/empty-front-matter-without-pragma.md new file mode 100644 index 00000000..948225e7 --- /dev/null +++ b/tests/require-pragma/markdown/empty-front-matter-without-pragma.md @@ -0,0 +1,4 @@ +--- +--- + +I should stay as-is !! diff --git a/tests/require-pragma/markdown/front-matter-with-pragma.md b/tests/require-pragma/markdown/front-matter-with-pragma.md new file mode 100644 index 00000000..7c8434af --- /dev/null +++ b/tests/require-pragma/markdown/front-matter-with-pragma.md @@ -0,0 +1,7 @@ +--- +something +--- + + + +I should be formatted !! diff --git a/tests/require-pragma/markdown/front-matter-without-pragma.md b/tests/require-pragma/markdown/front-matter-without-pragma.md new file mode 100644 index 00000000..d62b8974 --- /dev/null +++ b/tests/require-pragma/markdown/front-matter-without-pragma.md @@ -0,0 +1,5 @@ +--- +something +--- + +I should stay as-is !! diff --git a/tests/require-pragma/markdown/jsfmt.spec.js b/tests/require-pragma/markdown/jsfmt.spec.js new file mode 100644 index 00000000..8e45ea72 --- /dev/null +++ b/tests/require-pragma/markdown/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["markdown"], { requirePragma: true }); diff --git a/tests/require-pragma/markdown/with-pragma-in-multiline.md b/tests/require-pragma/markdown/with-pragma-in-multiline.md new file mode 100644 index 00000000..634e3704 --- /dev/null +++ b/tests/require-pragma/markdown/with-pragma-in-multiline.md @@ -0,0 +1,9 @@ + + +I should be formatted !! diff --git a/tests/require-pragma/markdown/with-pragma.md b/tests/require-pragma/markdown/with-pragma.md new file mode 100644 index 00000000..a3b68e94 --- /dev/null +++ b/tests/require-pragma/markdown/with-pragma.md @@ -0,0 +1,3 @@ + + +I should be formatted !! diff --git a/tests/require-pragma/markdown/without-pragma.md b/tests/require-pragma/markdown/without-pragma.md new file mode 100644 index 00000000..df06f622 --- /dev/null +++ b/tests/require-pragma/markdown/without-pragma.md @@ -0,0 +1 @@ +I should stay as-is !! diff --git a/tests/return/__snapshots__/jsfmt.spec.js.snap b/tests/return/__snapshots__/jsfmt.spec.js.snap index 44eb387f..5f9bb854 100644 --- a/tests/return/__snapshots__/jsfmt.spec.js.snap +++ b/tests/return/__snapshots__/jsfmt.spec.js.snap @@ -55,9 +55,55 @@ exports[`comment.js 1`] = ` function f() { return /* a */; } + +function x() { + return func2 + //comment + .bar(); +} + +function f() { + return ( + foo + // comment + .bar() + ); +} + +fn(function f() { + return ( + foo + // comment + .bar() + ); +}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ function f() { return /* a */; } +function x() { + return ( + func2 + //comment + .bar() + ); +} + +function f() { + return ( + foo + // comment + .bar() + ); +} + +fn(function f() { + return ( + foo + // comment + .bar() + ); +}); + `; diff --git a/tests/return/comment.js b/tests/return/comment.js index 0313022e..06126f9f 100644 --- a/tests/return/comment.js +++ b/tests/return/comment.js @@ -1,3 +1,25 @@ function f() { return /* a */; } + +function x() { + return func2 + //comment + .bar(); +} + +function f() { + return ( + foo + // comment + .bar() + ); +} + +fn(function f() { + return ( + foo + // comment + .bar() + ); +}); diff --git a/tests/spread/__snapshots__/jsfmt.spec.js.snap b/tests/spread/__snapshots__/jsfmt.spec.js.snap index e4351cc5..bf3cdf37 100644 --- a/tests/spread/__snapshots__/jsfmt.spec.js.snap +++ b/tests/spread/__snapshots__/jsfmt.spec.js.snap @@ -3,8 +3,14 @@ exports[`spread.js 1`] = ` const foo = { ...(a || b) }; const foo2 = { ...a || b }; +const foo3 = { ...(a ? b : c) }; + +async () => ({ ...(await foo) }); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ const foo = { ...(a || b) }; const foo2 = { ...(a || b) }; +const foo3 = { ...(a ? b : c) }; + +async () => ({ ...(await foo) }); `; diff --git a/tests/spread/spread.js b/tests/spread/spread.js index 7cd0a237..516467c6 100644 --- a/tests/spread/spread.js +++ b/tests/spread/spread.js @@ -1,2 +1,5 @@ const foo = { ...(a || b) }; const foo2 = { ...a || b }; +const foo3 = { ...(a ? b : c) }; + +async () => ({ ...(await foo) }); diff --git a/tests/stylefmt/attr-selector/__snapshots__/jsfmt.spec.js.snap b/tests/stylefmt/attr-selector/__snapshots__/jsfmt.spec.js.snap index 65ee1da1..c417a66a 100644 --- a/tests/stylefmt/attr-selector/__snapshots__/jsfmt.spec.js.snap +++ b/tests/stylefmt/attr-selector/__snapshots__/jsfmt.spec.js.snap @@ -8,11 +8,11 @@ exports[`attr-selector.css 1`] = ` [ class*="test" ]{ color: silver} ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -[ data-sizes~="m:0" ] { +[data-sizes~="m:0"] { color: pink; } -[ class*="test" ] { +[class*="test"] { color: silver; } diff --git a/tests/stylefmt/readme/__snapshots__/jsfmt.spec.js.snap b/tests/stylefmt/readme/__snapshots__/jsfmt.spec.js.snap index f95ad45a..5a0f4505 100644 --- a/tests/stylefmt/readme/__snapshots__/jsfmt.spec.js.snap +++ b/tests/stylefmt/readme/__snapshots__/jsfmt.spec.js.snap @@ -29,7 +29,7 @@ padding: 12px } ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // mixin for clearfix -@mixin clearfix () { +@mixin clearfix() { &:before, &:after { content: " "; diff --git a/tests/switch/__snapshots__/jsfmt.spec.js.snap b/tests/switch/__snapshots__/jsfmt.spec.js.snap index 2f325581..d9e2ebc4 100644 --- a/tests/switch/__snapshots__/jsfmt.spec.js.snap +++ b/tests/switch/__snapshots__/jsfmt.spec.js.snap @@ -223,3 +223,71 @@ switch (1) { } `; + +exports[`switch.js 1`] = ` +switch (a) { + case 3: + alert( '3' ); + break; + case 4: + alert( '4' ); + break; + case 5: + alert( '5' ); + break; + default: + alert( 'default' ); +} + +switch (veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong) { + case 3: + alert( '3' ); + break; + default: + alert( 'default' ); +} + +switch (veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong > veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong) { + case 3: + alert( '3' ); + break; + default: + alert( 'default' ); +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +switch (a) { + case 3: + alert("3"); + break; + case 4: + alert("4"); + break; + case 5: + alert("5"); + break; + default: + alert("default"); +} + +switch ( + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong +) { + case 3: + alert("3"); + break; + default: + alert("default"); +} + +switch ( + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong > + veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong +) { + case 3: + alert("3"); + break; + default: + alert("default"); +} + +`; diff --git a/tests/switch/switch.js b/tests/switch/switch.js new file mode 100644 index 00000000..c4c7ac13 --- /dev/null +++ b/tests/switch/switch.js @@ -0,0 +1,29 @@ +switch (a) { + case 3: + alert( '3' ); + break; + case 4: + alert( '4' ); + break; + case 5: + alert( '5' ); + break; + default: + alert( 'default' ); +} + +switch (veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong) { + case 3: + alert( '3' ); + break; + default: + alert( 'default' ); +} + +switch (veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong > veryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryVeryLong) { + case 3: + alert( '3' ); + break; + default: + alert( 'default' ); +} diff --git a/tests/ternaries/__snapshots__/jsfmt.spec.js.snap b/tests/ternaries/__snapshots__/jsfmt.spec.js.snap index 5eff19d9..67dc398c 100644 --- a/tests/ternaries/__snapshots__/jsfmt.spec.js.snap +++ b/tests/ternaries/__snapshots__/jsfmt.spec.js.snap @@ -105,36 +105,1330 @@ room = room.map((row, rowIndex) => `; +exports[`binary.js 4`] = ` +const funnelSnapshotCard = (report === MY_OVERVIEW && + !ReportGK.xar_metrics_active_capitol_v2) || + (report === COMPANY_OVERVIEW && + !ReportGK.xar_metrics_active_capitol_v2_company_metrics) + ? + : null; + +room = room.map((row, rowIndex) => ( + row.map((col, colIndex) => ( + (rowIndex === 0 || colIndex === 0 || rowIndex === height || colIndex === width) ? 1 : 0 + )) +)) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const funnelSnapshotCard = + (report === MY_OVERVIEW && !ReportGK.xar_metrics_active_capitol_v2) || + (report === COMPANY_OVERVIEW && + !ReportGK.xar_metrics_active_capitol_v2_company_metrics) ? ( + + ) : null; + +room = room.map((row, rowIndex) => + row.map( + (col, colIndex) => + rowIndex === 0 || + colIndex === 0 || + rowIndex === height || + colIndex === width + ? 1 + : 0 + ) +); + +`; + exports[`indent.js 1`] = ` aaaaaaaaaaaaaaa ? bbbbbbbbbbbbbbbbbb : ccccccccccccccc ? ddddddddddddddd : eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +a + ? { + a: 0 + } + : { + a: { + a: 0 + } + ? { + a: 0 + } + : { + y: { + a: 0 + } + ? { + a: 0 + } + : { + a: 0 + } + } + } + +a + ? { + a: function() { + return a + ? { + a: [ + a + ? { + a: 0, + b: [ + a + ? [ + 0, + 1 + ] + : [] + ] + } + : [ + [ + 0, + { + a: 0 + }, + a + ? 0 + : 1 + ], + function() { + return a + ? { + a: 0 + } + : [ + { + a: 0 + }, + {} + ]; + } + ] + ] + } + : [ + a + ? function() { + a + ? a( + a + ? { + a: a( + { + a: 0 + } + ) + } + : [ + 0, + a(), + a( + a(), + { + a: 0 + }, + a + ? a() + : a( + { + a: 0 + } + ) + ), + a() + ? { + a: a(), + b: [] + } + : {} + ] + ): + a( + a() + ? { + a: 0 + } + : (function(a) { + return a() + ? [ + { + a: 0, + b: a() + } + ] + : a( + [ + a + ? { + a: 0 + } + : {}, + { + a: 0 + } + ] + ); + })( + a + ? function(a) { + return function() { + return 0; + }; + } + : function(a) { + return function() { + return 1; + } + } + ) + ); + } + : function() { + + } + ]; + } + } + : a; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ aaaaaaaaaaaaaaa ? bbbbbbbbbbbbbbbbbb : ccccccccccccccc ? ddddddddddddddd - : eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg; + : eeeeeeeeeeeeeee + ? fffffffffffffff + : gggggggggggggggg; + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + +a + ? { + a: 0 + } + : { + a: { + a: 0 + } + ? { + a: 0 + } + : { + y: { + a: 0 + } + ? { + a: 0 + } + : { + a: 0 + } + } + }; + +a + ? { + a: function() { + return a + ? { + a: [ + a + ? { + a: 0, + b: [a ? [0, 1] : []] + } + : [ + [ + 0, + { + a: 0 + }, + a ? 0 : 1 + ], + function() { + return a + ? { + a: 0 + } + : [ + { + a: 0 + }, + {} + ]; + } + ] + ] + } + : [ + a + ? function() { + a + ? a( + a + ? { + a: a({ + a: 0 + }) + } + : [ + 0, + a(), + a( + a(), + { + a: 0 + }, + a + ? a() + : a({ + a: 0 + }) + ), + a() + ? { + a: a(), + b: [] + } + : {} + ] + ) + : a( + a() + ? { + a: 0 + } + : (function(a) { + return a() + ? [ + { + a: 0, + b: a() + } + ] + : a([ + a + ? { + a: 0 + } + : {}, + { + a: 0 + } + ]); + })( + a + ? function(a) { + return function() { + return 0; + }; + } + : function(a) { + return function() { + return 1; + }; + } + ) + ); + } + : function() {} + ]; + } + } + : a; `; exports[`indent.js 2`] = ` aaaaaaaaaaaaaaa ? bbbbbbbbbbbbbbbbbb : ccccccccccccccc ? ddddddddddddddd : eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +a + ? { + a: 0 + } + : { + a: { + a: 0 + } + ? { + a: 0 + } + : { + y: { + a: 0 + } + ? { + a: 0 + } + : { + a: 0 + } + } + } + +a + ? { + a: function() { + return a + ? { + a: [ + a + ? { + a: 0, + b: [ + a + ? [ + 0, + 1 + ] + : [] + ] + } + : [ + [ + 0, + { + a: 0 + }, + a + ? 0 + : 1 + ], + function() { + return a + ? { + a: 0 + } + : [ + { + a: 0 + }, + {} + ]; + } + ] + ] + } + : [ + a + ? function() { + a + ? a( + a + ? { + a: a( + { + a: 0 + } + ) + } + : [ + 0, + a(), + a( + a(), + { + a: 0 + }, + a + ? a() + : a( + { + a: 0 + } + ) + ), + a() + ? { + a: a(), + b: [] + } + : {} + ] + ): + a( + a() + ? { + a: 0 + } + : (function(a) { + return a() + ? [ + { + a: 0, + b: a() + } + ] + : a( + [ + a + ? { + a: 0 + } + : {}, + { + a: 0 + } + ] + ); + })( + a + ? function(a) { + return function() { + return 0; + }; + } + : function(a) { + return function() { + return 1; + } + } + ) + ); + } + : function() { + + } + ]; + } + } + : a; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ aaaaaaaaaaaaaaa ? bbbbbbbbbbbbbbbbbb : ccccccccccccccc - ? ddddddddddddddd - : eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg; + ? ddddddddddddddd + : eeeeeeeeeeeeeee + ? fffffffffffffff + : gggggggggggggggg; + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + +a + ? { + a: 0 + } + : { + a: { + a: 0 + } + ? { + a: 0 + } + : { + y: { + a: 0 + } + ? { + a: 0 + } + : { + a: 0 + } + } + }; + +a + ? { + a: function() { + return a + ? { + a: [ + a + ? { + a: 0, + b: [a ? [0, 1] : []] + } + : [ + [ + 0, + { + a: 0 + }, + a ? 0 : 1 + ], + function() { + return a + ? { + a: 0 + } + : [ + { + a: 0 + }, + {} + ]; + } + ] + ] + } + : [ + a + ? function() { + a + ? a( + a + ? { + a: a({ + a: 0 + }) + } + : [ + 0, + a(), + a( + a(), + { + a: 0 + }, + a + ? a() + : a({ + a: 0 + }) + ), + a() + ? { + a: a(), + b: [] + } + : {} + ] + ) + : a( + a() + ? { + a: 0 + } + : (function(a) { + return a() + ? [ + { + a: 0, + b: a() + } + ] + : a([ + a + ? { + a: 0 + } + : {}, + { + a: 0 + } + ]); + })( + a + ? function(a) { + return function() { + return 0; + }; + } + : function(a) { + return function() { + return 1; + }; + } + ) + ); + } + : function() {} + ]; + } + } + : a; `; exports[`indent.js 3`] = ` aaaaaaaaaaaaaaa ? bbbbbbbbbbbbbbbbbb : ccccccccccccccc ? ddddddddddddddd : eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +a + ? { + a: 0 + } + : { + a: { + a: 0 + } + ? { + a: 0 + } + : { + y: { + a: 0 + } + ? { + a: 0 + } + : { + a: 0 + } + } + } + +a + ? { + a: function() { + return a + ? { + a: [ + a + ? { + a: 0, + b: [ + a + ? [ + 0, + 1 + ] + : [] + ] + } + : [ + [ + 0, + { + a: 0 + }, + a + ? 0 + : 1 + ], + function() { + return a + ? { + a: 0 + } + : [ + { + a: 0 + }, + {} + ]; + } + ] + ] + } + : [ + a + ? function() { + a + ? a( + a + ? { + a: a( + { + a: 0 + } + ) + } + : [ + 0, + a(), + a( + a(), + { + a: 0 + }, + a + ? a() + : a( + { + a: 0 + } + ) + ), + a() + ? { + a: a(), + b: [] + } + : {} + ] + ): + a( + a() + ? { + a: 0 + } + : (function(a) { + return a() + ? [ + { + a: 0, + b: a() + } + ] + : a( + [ + a + ? { + a: 0 + } + : {}, + { + a: 0 + } + ] + ); + })( + a + ? function(a) { + return function() { + return 0; + }; + } + : function(a) { + return function() { + return 1; + } + } + ) + ); + } + : function() { + + } + ]; + } + } + : a; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ aaaaaaaaaaaaaaa ? bbbbbbbbbbbbbbbbbb : ccccccccccccccc ? ddddddddddddddd - : eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg; + : eeeeeeeeeeeeeee + ? fffffffffffffff + : gggggggggggggggg; + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + +a + ? { + a: 0 + } + : { + a: { + a: 0 + } + ? { + a: 0 + } + : { + y: { + a: 0 + } + ? { + a: 0 + } + : { + a: 0 + } + } + }; + +a + ? { + a: function() { + return a + ? { + a: [ + a + ? { + a: 0, + b: [a ? [0, 1] : []] + } + : [ + [ + 0, + { + a: 0 + }, + a ? 0 : 1 + ], + function() { + return a + ? { + a: 0 + } + : [ + { + a: 0 + }, + {} + ]; + } + ] + ] + } + : [ + a + ? function() { + a + ? a( + a + ? { + a: a({ + a: 0 + }) + } + : [ + 0, + a(), + a( + a(), + { + a: 0 + }, + a + ? a() + : a({ + a: 0 + }) + ), + a() + ? { + a: a(), + b: [] + } + : {} + ] + ) + : a( + a() + ? { + a: 0 + } + : (function(a) { + return a() + ? [ + { + a: 0, + b: a() + } + ] + : a([ + a + ? { + a: 0 + } + : {}, + { + a: 0 + } + ]); + })( + a + ? function(a) { + return function() { + return 0; + }; + } + : function(a) { + return function() { + return 1; + }; + } + ) + ); + } + : function() {} + ]; + } + } + : a; + +`; + +exports[`indent.js 4`] = ` +aaaaaaaaaaaaaaa ? bbbbbbbbbbbbbbbbbb : ccccccccccccccc ? ddddddddddddddd : eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +a + ? { + a: 0 + } + : { + a: { + a: 0 + } + ? { + a: 0 + } + : { + y: { + a: 0 + } + ? { + a: 0 + } + : { + a: 0 + } + } + } + +a + ? { + a: function() { + return a + ? { + a: [ + a + ? { + a: 0, + b: [ + a + ? [ + 0, + 1 + ] + : [] + ] + } + : [ + [ + 0, + { + a: 0 + }, + a + ? 0 + : 1 + ], + function() { + return a + ? { + a: 0 + } + : [ + { + a: 0 + }, + {} + ]; + } + ] + ] + } + : [ + a + ? function() { + a + ? a( + a + ? { + a: a( + { + a: 0 + } + ) + } + : [ + 0, + a(), + a( + a(), + { + a: 0 + }, + a + ? a() + : a( + { + a: 0 + } + ) + ), + a() + ? { + a: a(), + b: [] + } + : {} + ] + ): + a( + a() + ? { + a: 0 + } + : (function(a) { + return a() + ? [ + { + a: 0, + b: a() + } + ] + : a( + [ + a + ? { + a: 0 + } + : {}, + { + a: 0 + } + ] + ); + })( + a + ? function(a) { + return function() { + return 0; + }; + } + : function(a) { + return function() { + return 1; + } + } + ) + ); + } + : function() { + + } + ]; + } + } + : a; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +aaaaaaaaaaaaaaa + ? bbbbbbbbbbbbbbbbbb + : ccccccccccccccc + ? ddddddddddddddd + : eeeeeeeeeeeeeee + ? fffffffffffffff + : gggggggggggggggg; + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + ? aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + : aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa; + +a + ? { + a: 0 + } + : { + a: { + a: 0 + } + ? { + a: 0 + } + : { + y: { + a: 0 + } + ? { + a: 0 + } + : { + a: 0 + } + } + }; + +a + ? { + a: function() { + return a + ? { + a: [ + a + ? { + a: 0, + b: [a ? [0, 1] : []] + } + : [ + [ + 0, + { + a: 0 + }, + a ? 0 : 1 + ], + function() { + return a + ? { + a: 0 + } + : [ + { + a: 0 + }, + {} + ]; + } + ] + ] + } + : [ + a + ? function() { + a + ? a( + a + ? { + a: a({ + a: 0 + }) + } + : [ + 0, + a(), + a( + a(), + { + a: 0 + }, + a + ? a() + : a({ + a: 0 + }) + ), + a() + ? { + a: a(), + b: [] + } + : {} + ] + ) + : a( + a() + ? { + a: 0 + } + : (function(a) { + return a() + ? [ + { + a: 0, + b: a() + } + ] + : a([ + a + ? { + a: 0 + } + : {}, + { + a: 0 + } + ]); + })( + a + ? function( + a + ) { + return function() { + return 0; + }; + } + : function( + a + ) { + return function() { + return 1; + }; + } + ) + ); + } + : function() {} + ]; + } + } + : a; `; @@ -174,6 +1468,18 @@ let icecream = `; +exports[`nested.js 4`] = ` +let icecream = what == "cone" + ? p => !!p ? \`here's your \${p} cone\` : \`just the empty cone for you\` + : p => \`here's your \${p} \${what}\`; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +let icecream = + what == "cone" + ? p => (!!p ? \`here's your \${p} cone\` : \`just the empty cone for you\`) + : p => \`here's your \${p} \${what}\`; + +`; + exports[`parenthesis.js 1`] = ` debug ? this.state.isVisible ? "partially visible" : "hidden" : null; debug ? this.state.isVisible && somethingComplex ? "partially visible" : "hidden" : null; @@ -184,7 +1490,9 @@ a => a ? aasdasdasdasdasdasdaaasdasdasdasdasdasdasdasdasdasdasdasdasdaaaaaa : a ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ debug ? (this.state.isVisible ? "partially visible" : "hidden") : null; debug - ? this.state.isVisible && somethingComplex ? "partially visible" : "hidden" + ? this.state.isVisible && somethingComplex + ? "partially visible" + : "hidden" : null; a => @@ -211,7 +1519,9 @@ a => a ? aasdasdasdasdasdasdaaasdasdasdasdasdasdasdasdasdasdasdasdasdaaaaaa : a ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ debug ? (this.state.isVisible ? "partially visible" : "hidden") : null; debug - ? this.state.isVisible && somethingComplex ? "partially visible" : "hidden" + ? this.state.isVisible && somethingComplex + ? "partially visible" + : "hidden" : null; a => @@ -238,17 +1548,48 @@ a => a ? aasdasdasdasdasdasdaaasdasdasdasdasdasdasdasdasdasdasdasdasdaaaaaa : a ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ debug ? (this.state.isVisible ? "partially visible" : "hidden") : null; debug - ? this.state.isVisible && somethingComplex ? "partially visible" : "hidden" + ? this.state.isVisible && somethingComplex + ? "partially visible" + : "hidden" : null; a => a ? () => { a; - } + } : () => { a; - }; + }; +a => (a ? a : a); +a => + a ? aasdasdasdasdasdasdaaasdasdasdasdasdasdasdasdasdasdasdasdasdaaaaaa : a; + +`; + +exports[`parenthesis.js 4`] = ` +debug ? this.state.isVisible ? "partially visible" : "hidden" : null; +debug ? this.state.isVisible && somethingComplex ? "partially visible" : "hidden" : null; + +a => a ? () => {a} : () => {a} +a => a ? a : a +a => a ? aasdasdasdasdasdasdaaasdasdasdasdasdasdasdasdasdasdasdasdasdaaaaaa : a +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +debug ? (this.state.isVisible ? "partially visible" : "hidden") : null; +debug + ? this.state.isVisible && somethingComplex + ? "partially visible" + : "hidden" + : null; + +a => + a + ? () => { + a; + } + : () => { + a; + }; a => (a ? a : a); a => a ? aasdasdasdasdasdasdaaasdasdasdasdasdasdasdasdasdasdasdasdasdaaaaaa : a; @@ -388,7 +1729,7 @@ const obj3 = conditionIsTruthy object: "with", lots: "of", stuff - } + } : shortThing; const obj4 = conditionIsTruthy @@ -398,7 +1739,7 @@ const obj4 = conditionIsTruthy object: "with", lots: "of", stuff - }; + }; const obj5 = conditionIsTruthy ? { some: "long", object: "with", lots: "of", stuff } @@ -407,6 +1748,58 @@ const obj5 = conditionIsTruthy object: "with", lots: "of", stuff - }; + }; + +`; + +exports[`test.js 4`] = ` +const obj0 = conditionIsTruthy ? shortThing : otherShortThing + +const obj1 = conditionIsTruthy ? { some: 'long', object: 'with', lots: 'of', stuff } : shortThing + +const obj2 = conditionIsTruthy ? shortThing : { some: 'long', object: 'with', lots: 'of', stuff } + +const obj3 = conditionIsTruthy ? { some: 'eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger', object: 'with', lots: 'of', stuff } : shortThing + +const obj4 = conditionIsTruthy ? shortThing : { some: 'eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger', object: 'with', lots: 'of', stuff } + +const obj5 = conditionIsTruthy ? { some: 'long', object: 'with', lots: 'of', stuff } : { some: 'eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger', object: 'with', lots: 'of', stuff } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +const obj0 = conditionIsTruthy ? shortThing : otherShortThing; + +const obj1 = conditionIsTruthy + ? { some: "long", object: "with", lots: "of", stuff } + : shortThing; + +const obj2 = conditionIsTruthy + ? shortThing + : { some: "long", object: "with", lots: "of", stuff }; + +const obj3 = conditionIsTruthy + ? { + some: "eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger", + object: "with", + lots: "of", + stuff + } + : shortThing; + +const obj4 = conditionIsTruthy + ? shortThing + : { + some: "eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger", + object: "with", + lots: "of", + stuff + }; + +const obj5 = conditionIsTruthy + ? { some: "long", object: "with", lots: "of", stuff } + : { + some: "eeeeeeeeeeeeven looooooooooooooooooooooooooooooonger", + object: "with", + lots: "of", + stuff + }; `; diff --git a/tests/ternaries/indent.js b/tests/ternaries/indent.js index b6b2380b..e2ace47a 100644 --- a/tests/ternaries/indent.js +++ b/tests/ternaries/indent.js @@ -1 +1,167 @@ aaaaaaaaaaaaaaa ? bbbbbbbbbbbbbbbbbb : ccccccccccccccc ? ddddddddddddddd : eeeeeeeeeeeeeee ? fffffffffffffff : gggggggggggggggg + +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +? +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa +: +aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa + +a + ? { + a: 0 + } + : { + a: { + a: 0 + } + ? { + a: 0 + } + : { + y: { + a: 0 + } + ? { + a: 0 + } + : { + a: 0 + } + } + } + +a + ? { + a: function() { + return a + ? { + a: [ + a + ? { + a: 0, + b: [ + a + ? [ + 0, + 1 + ] + : [] + ] + } + : [ + [ + 0, + { + a: 0 + }, + a + ? 0 + : 1 + ], + function() { + return a + ? { + a: 0 + } + : [ + { + a: 0 + }, + {} + ]; + } + ] + ] + } + : [ + a + ? function() { + a + ? a( + a + ? { + a: a( + { + a: 0 + } + ) + } + : [ + 0, + a(), + a( + a(), + { + a: 0 + }, + a + ? a() + : a( + { + a: 0 + } + ) + ), + a() + ? { + a: a(), + b: [] + } + : {} + ] + ): + a( + a() + ? { + a: 0 + } + : (function(a) { + return a() + ? [ + { + a: 0, + b: a() + } + ] + : a( + [ + a + ? { + a: 0 + } + : {}, + { + a: 0 + } + ] + ); + })( + a + ? function(a) { + return function() { + return 0; + }; + } + : function(a) { + return function() { + return 1; + } + } + ) + ); + } + : function() { + + } + ]; + } + } + : a; diff --git a/tests/ternaries/jsfmt.spec.js b/tests/ternaries/jsfmt.spec.js index db53feeb..3f4ea6ad 100644 --- a/tests/ternaries/jsfmt.spec.js +++ b/tests/ternaries/jsfmt.spec.js @@ -1,3 +1,4 @@ run_spec(__dirname, ["flow", "typescript"]); run_spec(__dirname, ["flow", "typescript"], { tabWidth: 4 }); run_spec(__dirname, ["flow", "typescript"], { useTabs: true }); +run_spec(__dirname, ["flow", "typescript"], { useTabs: true, tabWidth: 4 }); diff --git a/tests/test_declarations/__snapshots__/jsfmt.spec.js.snap b/tests/test_declarations/__snapshots__/jsfmt.spec.js.snap index 46af9f6b..d75b3e9e 100644 --- a/tests/test_declarations/__snapshots__/jsfmt.spec.js.snap +++ b/tests/test_declarations/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,107 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`angular_async.js 1`] = ` +beforeEach(async(() => { + // code +})); + +afterAll(async(() => { + console.log('Hello'); +})); + +it('should create the app', async(() => { + //code +})); + +it("does something really long and complicated so I have to write a very long name for the test", async(() => { + // code +})); + +/* +* isTestCall(parent) should only be called when parent exists +* and parent.type is CallExpression. This test makes sure that +* no errors are thrown when calling isTestCall(parent) +*/ +function x() { async(() => {}) } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +beforeEach(async(() => { + // code +})); + +afterAll(async(() => { + console.log("Hello"); +})); + +it("should create the app", async(() => { + //code +})); + +it("does something really long and complicated so I have to write a very long name for the test", async(() => { + // code +})); + +/* +* isTestCall(parent) should only be called when parent exists +* and parent.type is CallExpression. This test makes sure that +* no errors are thrown when calling isTestCall(parent) +*/ +function x() { + async(() => {}); +} + +`; + +exports[`angular_async.js 2`] = ` +beforeEach(async(() => { + // code +})); + +afterAll(async(() => { + console.log('Hello'); +})); + +it('should create the app', async(() => { + //code +})); + +it("does something really long and complicated so I have to write a very long name for the test", async(() => { + // code +})); + +/* +* isTestCall(parent) should only be called when parent exists +* and parent.type is CallExpression. This test makes sure that +* no errors are thrown when calling isTestCall(parent) +*/ +function x() { async(() => {}) } +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +beforeEach(async(() => { + // code +})); + +afterAll(async(() => { + console.log("Hello"); +})); + +it("should create the app", async(() => { + //code +})); + +it("does something really long and complicated so I have to write a very long name for the test", async(() => { + // code +})); + +/* +* isTestCall(parent) should only be called when parent exists +* and parent.type is CallExpression. This test makes sure that +* no errors are thrown when calling isTestCall(parent) +*/ +function x() { + async(() => {}); +} + +`; + exports[`test_declarations.js 1`] = ` // Shouldn't break @@ -88,6 +190,10 @@ ftest("does something really long and complicated so I have to write a very long xtest("does something really long and complicated so I have to write a very long name for the describe block", () => {}); +skip(\`does something really long and complicated so I have to write a very long name for the test\`, () => {}); + +skip("does something really long and complicated so I have to write a very long name for the test", () => {}); + // Should break it.only("does something really long and complicated so I have to write a very long name for the test", 10, () => { @@ -101,6 +207,8 @@ it.only.only("does something really long and complicated so I have to write a ve it.only.only("does something really long and complicated so I have to write a very long name for the test", (a, b, c) => { console.log("hello!"); }); + +xskip("does something really long and complicated so I have to write a very long name for the test", () => {}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Shouldn't break @@ -189,6 +297,10 @@ ftest("does something really long and complicated so I have to write a very long xtest("does something really long and complicated so I have to write a very long name for the describe block", () => {}); +skip(\`does something really long and complicated so I have to write a very long name for the test\`, () => {}); + +skip("does something really long and complicated so I have to write a very long name for the test", () => {}); + // Should break it.only( @@ -213,6 +325,11 @@ it.only.only( } ); +xskip( + "does something really long and complicated so I have to write a very long name for the test", + () => {} +); + `; exports[`test_declarations.js 2`] = ` @@ -303,6 +420,10 @@ ftest("does something really long and complicated so I have to write a very long xtest("does something really long and complicated so I have to write a very long name for the describe block", () => {}); +skip(\`does something really long and complicated so I have to write a very long name for the test\`, () => {}); + +skip("does something really long and complicated so I have to write a very long name for the test", () => {}); + // Should break it.only("does something really long and complicated so I have to write a very long name for the test", 10, () => { @@ -316,6 +437,8 @@ it.only.only("does something really long and complicated so I have to write a ve it.only.only("does something really long and complicated so I have to write a very long name for the test", (a, b, c) => { console.log("hello!"); }); + +xskip("does something really long and complicated so I have to write a very long name for the test", () => {}); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Shouldn't break @@ -404,6 +527,10 @@ ftest("does something really long and complicated so I have to write a very long xtest("does something really long and complicated so I have to write a very long name for the describe block", () => {}); +skip(\`does something really long and complicated so I have to write a very long name for the test\`, () => {}); + +skip("does something really long and complicated so I have to write a very long name for the test", () => {}); + // Should break it.only( @@ -428,4 +555,9 @@ it.only.only( } ); +xskip( + "does something really long and complicated so I have to write a very long name for the test", + () => {} +); + `; diff --git a/tests/test_declarations/angular_async.js b/tests/test_declarations/angular_async.js new file mode 100644 index 00000000..fad4aa76 --- /dev/null +++ b/tests/test_declarations/angular_async.js @@ -0,0 +1,22 @@ +beforeEach(async(() => { + // code +})); + +afterAll(async(() => { + console.log('Hello'); +})); + +it('should create the app', async(() => { + //code +})); + +it("does something really long and complicated so I have to write a very long name for the test", async(() => { + // code +})); + +/* +* isTestCall(parent) should only be called when parent exists +* and parent.type is CallExpression. This test makes sure that +* no errors are thrown when calling isTestCall(parent) +*/ +function x() { async(() => {}) } diff --git a/tests/test_declarations/test_declarations.js b/tests/test_declarations/test_declarations.js index e7b33130..f4978419 100644 --- a/tests/test_declarations/test_declarations.js +++ b/tests/test_declarations/test_declarations.js @@ -85,6 +85,10 @@ ftest("does something really long and complicated so I have to write a very long xtest("does something really long and complicated so I have to write a very long name for the describe block", () => {}); +skip(`does something really long and complicated so I have to write a very long name for the test`, () => {}); + +skip("does something really long and complicated so I have to write a very long name for the test", () => {}); + // Should break it.only("does something really long and complicated so I have to write a very long name for the test", 10, () => { @@ -98,3 +102,5 @@ it.only.only("does something really long and complicated so I have to write a ve it.only.only("does something really long and complicated so I have to write a very long name for the test", (a, b, c) => { console.log("hello!"); }); + +xskip("does something really long and complicated so I have to write a very long name for the test", () => {}); diff --git a/tests/typescript/custom/abstract/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/custom/abstract/__snapshots__/jsfmt.spec.js.snap index 8b7d1e6a..4bd46988 100644 --- a/tests/typescript/custom/abstract/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/custom/abstract/__snapshots__/jsfmt.spec.js.snap @@ -61,3 +61,22 @@ abstract class Foo { } `; + +exports[`abstractPropertiesWithBreaks.ts 1`] = ` +abstract class Foo { + abstract private foobar = + // comment to break + 1 + + // another comment to break + 2; +} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +abstract class Foo { + private abstract foobar = + // comment to break + 1 + + // another comment to break + 2; +} + +`; diff --git a/tests/typescript/custom/abstract/abstractPropertiesWithBreaks.ts b/tests/typescript/custom/abstract/abstractPropertiesWithBreaks.ts new file mode 100644 index 00000000..d238f528 --- /dev/null +++ b/tests/typescript/custom/abstract/abstractPropertiesWithBreaks.ts @@ -0,0 +1,7 @@ +abstract class Foo { + abstract private foobar = + // comment to break + 1 + + // another comment to break + 2; +} diff --git a/tests/typescript/custom/modifiers/__snapshots__/jsfmt.spec.js.snap b/tests/typescript/custom/modifiers/__snapshots__/jsfmt.spec.js.snap index a95c3d80..6aae1703 100644 --- a/tests/typescript/custom/modifiers/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript/custom/modifiers/__snapshots__/jsfmt.spec.js.snap @@ -1,5 +1,20 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`minustoken.ts 1`] = ` +type MutableRequired = { +-readonly [P in keyof T]-?:T[P] +}; // Remove readonly and ? + +type ReadonlyPartial = { ++readonly [P in keyof T]+?:T[P] +}; // Add readonly and ? +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type MutableRequired = { -readonly [P in keyof T]-?: T[P] }; // Remove readonly and ? + +type ReadonlyPartial = { +readonly [P in keyof T]+?: T[P] }; // Add readonly and ? + +`; + exports[`question.ts 1`] = ` var x: { [A in keyof B]?: any; diff --git a/tests/typescript/custom/modifiers/minustoken.ts b/tests/typescript/custom/modifiers/minustoken.ts new file mode 100644 index 00000000..80bdaa5c --- /dev/null +++ b/tests/typescript/custom/modifiers/minustoken.ts @@ -0,0 +1,7 @@ +type MutableRequired = { +-readonly [P in keyof T]-?:T[P] +}; // Remove readonly and ? + +type ReadonlyPartial = { ++readonly [P in keyof T]+?:T[P] +}; // Add readonly and ? diff --git a/tests/typescript_as/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_as/__snapshots__/jsfmt.spec.js.snap index 25d5bea9..014f93fe 100644 --- a/tests/typescript_as/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript_as/__snapshots__/jsfmt.spec.js.snap @@ -7,7 +7,7 @@ this.isTabActionBar((e.target || e.srcElement) as HTMLElement); 'current' in (props.pagination as Object) start + (yearSelectTotal as number) scrollTop > (visibilityHeight as number) -export default class Column extends (RcTable.Column as React.ComponentClass>) {} +export default class Column extends (RcTable.Column as React.ComponentClass,ColumnProps,ColumnProps,ColumnProps>) {} export abstract class MobxTypedForm extends (Form as { new (): any }) {} ({}) as {}; function*g() { @@ -24,6 +24,7 @@ const state = JSON.stringify({ } as State); (foo.bar as Baz) = [bar]; +(foo.bar as any)++; (bValue as boolean) ? 0 : -1; bValue ? 0 : -1; @@ -38,6 +39,9 @@ this.isTabActionBar((e.target || e.srcElement) as HTMLElement); start + (yearSelectTotal as number); scrollTop > (visibilityHeight as number); export default class Column extends (RcTable.Column as React.ComponentClass< + ColumnProps, + ColumnProps, + ColumnProps, ColumnProps >) {} export abstract class MobxTypedForm extends (Form as { new (): any }) {} @@ -56,6 +60,7 @@ const state = JSON.stringify({ } as State); (foo.bar as Baz) = [bar]; +(foo.bar as any)++; (bValue as boolean) ? 0 : -1; bValue ? 0 : -1; diff --git a/tests/typescript_as/as.js b/tests/typescript_as/as.js index b74f5f8a..464285c0 100644 --- a/tests/typescript_as/as.js +++ b/tests/typescript_as/as.js @@ -4,7 +4,7 @@ this.isTabActionBar((e.target || e.srcElement) as HTMLElement); 'current' in (props.pagination as Object) start + (yearSelectTotal as number) scrollTop > (visibilityHeight as number) -export default class Column extends (RcTable.Column as React.ComponentClass>) {} +export default class Column extends (RcTable.Column as React.ComponentClass,ColumnProps,ColumnProps,ColumnProps>) {} export abstract class MobxTypedForm extends (Form as { new (): any }) {} ({}) as {}; function*g() { @@ -21,6 +21,7 @@ const state = JSON.stringify({ } as State); (foo.bar as Baz) = [bar]; +(foo.bar as any)++; (bValue as boolean) ? 0 : -1; bValue ? 0 : -1; diff --git a/tests/typescript_class/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_class/__snapshots__/jsfmt.spec.js.snap index adf74216..861224dd 100644 --- a/tests/typescript_class/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript_class/__snapshots__/jsfmt.spec.js.snap @@ -28,6 +28,97 @@ class F<__T> {} `; +exports[`extends_implements.ts 1`] = ` +class Class extends AbstractClass implements Interface1, Interface2, Interface3, Interface4 {} + +class ExtendsAbstractClassAndImplementsInterfaces extends AbstractClass + implements Interface1, Interface2, Interface3, Interface4 {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass + implements Interface1, Interface2, Interface3, Interface4 {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass + implements + Interface1, + Interface2, + Interface3, + Interface4, + Interface5, + Interface6, + Interface7, + Interface8 {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass + implements + Interface1, + Interface2, + Interface3, + Interface4, + Interface5, + Interface6, + Interface7, + Interface8 {} +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class Class extends AbstractClass + implements Interface1, Interface2, Interface3, Interface4 {} + +class ExtendsAbstractClassAndImplementsInterfaces extends AbstractClass + implements Interface1, Interface2, Interface3, Interface4 {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass + implements Interface1, Interface2, Interface3, Interface4 {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass + implements + Interface1, + Interface2, + Interface3, + Interface4, + Interface5, + Interface6, + Interface7, + Interface8 {} + +class ExtendsAbstractClassAndImplementsInterfaces extends AAAAAAAAAAAAAAbstractClass< + Type1, + Type2, + Type3, + Type4, + Type5, + Type6, + Type7 +> {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass< + Type1, + Type2, + Type3, + Type4, + Type5, + Type6, + Type7 + > + implements + Interface1, + Interface2, + Interface3, + Interface4, + Interface5, + Interface6, + Interface7, + Interface8 {} + +`; + exports[`generics.ts 1`] = ` class implements Map {} diff --git a/tests/typescript_class/extends_implements.ts b/tests/typescript_class/extends_implements.ts new file mode 100644 index 00000000..0daed035 --- /dev/null +++ b/tests/typescript_class/extends_implements.ts @@ -0,0 +1,35 @@ +class Class extends AbstractClass implements Interface1, Interface2, Interface3, Interface4 {} + +class ExtendsAbstractClassAndImplementsInterfaces extends AbstractClass + implements Interface1, Interface2, Interface3, Interface4 {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass + implements Interface1, Interface2, Interface3, Interface4 {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass + implements + Interface1, + Interface2, + Interface3, + Interface4, + Interface5, + Interface6, + Interface7, + Interface8 {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass {} + +class ExtendsAbstractClassAndImplementsInterfaces + extends AAAAAAAAAAAAAAbstractClass + implements + Interface1, + Interface2, + Interface3, + Interface4, + Interface5, + Interface6, + Interface7, + Interface8 {} diff --git a/tests/typescript_conditional_types/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_conditional_types/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..a06b23e6 --- /dev/null +++ b/tests/typescript_conditional_types/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`conditonal-types.ts 1`] = ` +export type DeepReadonly = T extends any[] ? DeepReadonlyArray : T extends object ? DeepReadonlyObject : T; + +type NonFunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T]; + +interface DeepReadonlyArray extends ReadonlyArray> {} + +type DeepReadonlyObject = { + readonly [P in NonFunctionPropertyNames]: DeepReadonly; +}; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +export type DeepReadonly = T extends any[] + ? DeepReadonlyArray + : T extends object ? DeepReadonlyObject : T; + +type NonFunctionPropertyNames = { + [K in keyof T]: T[K] extends Function ? never : K +}[keyof T]; + +interface DeepReadonlyArray extends ReadonlyArray> {} + +type DeepReadonlyObject = { + readonly [P in NonFunctionPropertyNames]: DeepReadonly +}; + +`; + +exports[`infer-type.ts 1`] = ` +type TestReturnType any> = T extends (...args: any[]) => infer R ? R : any; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type TestReturnType any> = T extends ( + ...args: any[] +) => infer R + ? R + : any; + +`; diff --git a/tests/typescript_conditional_types/conditonal-types.ts b/tests/typescript_conditional_types/conditonal-types.ts new file mode 100644 index 00000000..af1847f7 --- /dev/null +++ b/tests/typescript_conditional_types/conditonal-types.ts @@ -0,0 +1,9 @@ +export type DeepReadonly = T extends any[] ? DeepReadonlyArray : T extends object ? DeepReadonlyObject : T; + +type NonFunctionPropertyNames = { [K in keyof T]: T[K] extends Function ? never : K }[keyof T]; + +interface DeepReadonlyArray extends ReadonlyArray> {} + +type DeepReadonlyObject = { + readonly [P in NonFunctionPropertyNames]: DeepReadonly; +}; diff --git a/tests/typescript_conditional_types/infer-type.ts b/tests/typescript_conditional_types/infer-type.ts new file mode 100644 index 00000000..ed8e81c8 --- /dev/null +++ b/tests/typescript_conditional_types/infer-type.ts @@ -0,0 +1 @@ +type TestReturnType any> = T extends (...args: any[]) => infer R ? R : any; diff --git a/tests/typescript_conditional_types/jsfmt.spec.js b/tests/typescript_conditional_types/jsfmt.spec.js new file mode 100644 index 00000000..2ea3bb6e --- /dev/null +++ b/tests/typescript_conditional_types/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["typescript"]); diff --git a/tests/typescript_definite/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_definite/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..587be52b --- /dev/null +++ b/tests/typescript_definite/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,20 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`definite.ts 1`] = ` +class MyComponent { +ngModel!: ng.INgModelController; +} + +const x!: string + +var y!: MyComponent +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +class MyComponent { + ngModel!: ng.INgModelController; +} + +const x!: string; + +var y!: MyComponent; + +`; diff --git a/tests/typescript_definite/definite.ts b/tests/typescript_definite/definite.ts new file mode 100644 index 00000000..8febeb57 --- /dev/null +++ b/tests/typescript_definite/definite.ts @@ -0,0 +1,7 @@ +class MyComponent { +ngModel!: ng.INgModelController; +} + +const x!: string + +var y!: MyComponent diff --git a/tests/typescript_definite/jsfmt.spec.js b/tests/typescript_definite/jsfmt.spec.js new file mode 100644 index 00000000..2ea3bb6e --- /dev/null +++ b/tests/typescript_definite/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["typescript"]); diff --git a/tests/typescript_non_null/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_non_null/__snapshots__/jsfmt.spec.js.snap index 530b9477..ea63a6f9 100644 --- a/tests/typescript_non_null/__snapshots__/jsfmt.spec.js.snap +++ b/tests/typescript_non_null/__snapshots__/jsfmt.spec.js.snap @@ -4,6 +4,10 @@ exports[`member-chain.js 1`] = ` const { somePropThatHasAReallyLongName, anotherPropThatHasALongName } = this.props.imReallySureAboutThis!; const { somePropThatHasAReallyLongName, anotherPropThatHasALongName } = this.props.imReallySureAboutThis!.anotherObject; + +this.foo.get("bar")!.doThings().more(); + +foo!.bar().baz().what(); ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ const { somePropThatHasAReallyLongName, @@ -15,11 +19,22 @@ const { anotherPropThatHasALongName } = this.props.imReallySureAboutThis!.anotherObject; +this.foo + .get("bar")! + .doThings() + .more(); + +foo! + .bar() + .baz() + .what(); + `; exports[`parens.ts 1`] = ` (a ? b : c) ![tokenKey]; (a || b) ![tokenKey]; +(void 0)!; async function f() { return (await foo())!; @@ -31,6 +46,7 @@ function* g() { ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ (a ? b : c)![tokenKey]; (a || b)![tokenKey]; +(void 0)!; async function f() { return (await foo())!; diff --git a/tests/typescript_non_null/member-chain.js b/tests/typescript_non_null/member-chain.js index 8d8e5a14..7d6ba397 100644 --- a/tests/typescript_non_null/member-chain.js +++ b/tests/typescript_non_null/member-chain.js @@ -1,3 +1,7 @@ const { somePropThatHasAReallyLongName, anotherPropThatHasALongName } = this.props.imReallySureAboutThis!; const { somePropThatHasAReallyLongName, anotherPropThatHasALongName } = this.props.imReallySureAboutThis!.anotherObject; + +this.foo.get("bar")!.doThings().more(); + +foo!.bar().baz().what(); diff --git a/tests/typescript_non_null/parens.ts b/tests/typescript_non_null/parens.ts index 24d3d108..93ad2f0c 100644 --- a/tests/typescript_non_null/parens.ts +++ b/tests/typescript_non_null/parens.ts @@ -1,5 +1,6 @@ (a ? b : c) ![tokenKey]; (a || b) ![tokenKey]; +(void 0)!; async function f() { return (await foo())!; diff --git a/tests/typescript_unique_symbol/__snapshots__/jsfmt.spec.js.snap b/tests/typescript_unique_symbol/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..c40caca0 --- /dev/null +++ b/tests/typescript_unique_symbol/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,10 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`unique-symbol.ts 1`] = ` +type A = unique symbol; +type B = keyof something; +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +type A = unique symbol; +type B = keyof something; + +`; diff --git a/tests/typescript_unique_symbol/jsfmt.spec.js b/tests/typescript_unique_symbol/jsfmt.spec.js new file mode 100644 index 00000000..2ea3bb6e --- /dev/null +++ b/tests/typescript_unique_symbol/jsfmt.spec.js @@ -0,0 +1 @@ +run_spec(__dirname, ["typescript"]); diff --git a/tests/typescript_unique_symbol/unique-symbol.ts b/tests/typescript_unique_symbol/unique-symbol.ts new file mode 100644 index 00000000..3054f6b7 --- /dev/null +++ b/tests/typescript_unique_symbol/unique-symbol.ts @@ -0,0 +1,2 @@ +type A = unique symbol; +type B = keyof something; diff --git a/tests/vue_examples/__snapshots__/jsfmt.spec.js.snap b/tests/vue_examples/__snapshots__/jsfmt.spec.js.snap new file mode 100644 index 00000000..d6252633 --- /dev/null +++ b/tests/vue_examples/__snapshots__/jsfmt.spec.js.snap @@ -0,0 +1,421 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`board_card.vue 1`] = ` + + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +`; + +exports[`board_card.vue 2`] = ` + + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +`; + +exports[`self_closing.vue 1`] = ` + + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +`; + +exports[`self_closing.vue 2`] = ` + + + +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + + + + +`; + +exports[`self_closing_style.vue 1`] = ` + + + + + + + + + +``` + +#### Fix `@nest` at-rule ([#3975] by [@evilebottnawi]) + +Prettier was mistakenly adding spaces around the `&` character in selectors passed to `@nest`, which changed which selector the rule compiled into. This has been fixed: + + +```scss +// Unformatted code: +.title { + @nest :global(h1)& { + background: red; + } +} + +// Formatted code (Prettier 1.10.2): +.title { + @nest :global(h1) & { + background: red; + } +} + +// Formatted code (Prettier 1.11.0): +.title { + @nest :global(h1)& { + background: red; + } +} +``` + +#### Format `progid:DXImageTransform.Microsoft.gradient` as-is ([#4028] by [@evilebottnawi]) + +In Prettier 1.10.2, we had a lot of logic to handle formatting of Microsoft-browser-specific filter values, but they still weren't formatting very well. Since they are non-standard and infrequently used, we've decided to print them as-is without attempting to format them. + +#### Don't lowercase nesting selector ([#4048] by [@evilebottnawi]) + +In Prettier 1.10.2, we were incorrectly lower-casing nesting SCSS selectors: + + +```scss +// Unformatted code: +.foo { + &-1Bar { + } +} + +// Formatted code: (Prettier 1.10.2): +.foo { + &-1bar { + } +} +``` + +This has been fixed in Prettier 1.11.0: + + +```scss +// Formatted code: (Prettier 1.11.0): +.foo { + &-1Bar { + } +} + +``` + +### JavaScript + +#### Don't print odd-looking parens around return argument with comment ([#3665] by [@duailibe]) + +Prettier was printing parens around the argument to a `return` statement in an odd way when the argument contained a comment. These parens are now formatted better. + + +```js +// Unformatted code: +func1(function() { + return func2 + //comment + .bar(); +}); + +// Formatted code (Prettier 1.10.2): +func1(function() { + return (func2 + //comment + .bar() ); +}); + +// Formatted code (Prettier 1.11.0): +func1(function() { + return ( + func2 + //comment + .bar() + ); +}); +``` + +#### Respect 0-indent markdown-in-js ([#3676] by [@ikatyang]) + +Prettier's markdown-in-js formatting (triggered by using a template literal tag named `md` was inserting indentation): + + +```js +// Unformatted code +md` +This is some markdown +` + +// Formatted code (Prettier 1.10.2) +md` + This is some markdown +`; +``` + +This was desired and acceptable behavior for many markdown template tag functions, such as `react-markings`, but it broke some other in-house markdown formatters. + +Prettier 1.11.0 will now respect the indentation level you set in your markdown, so users of template tag functions like the one provided by `react-markings` can continue to indent their code, and users of other template tag functions which do not support indentation can choose not to. + +#### Format AMD define calls like CJS require calls ([#3830] by [@salemhilal]) + +When using AMD modules, one pattern is to use `define` to specify the dependencies of an anonymous module like so: + + +```js +define([ + "jquery", + "common/global.context", + "common/log.event", + "some_project/square", + "some_project/rectangle", + "some_project/triangle", + "some_project/circle", + "some_project/star" +], function($, Context, EventLogger, Square, Rectangle, Triangle, Circle, Star) { + console.log("some code"); +}); +``` + +Prettier 1.10.2 was breaking this onto multiple lines, which introduced a lot of top-level indentation: + + +```js +define( + [ + "jquery", + "common/global.context", + "common/log.event", + "some_project/square", + "some_project/rectangle", + "some_project/triangle", + "some_project/circle", + "some_project/star" + ], + function($, Context, EventLogger, Square, Rectangle, Triangle, Circle, Star) { + console.log("some code"); + } +); +``` + +Prettier 1.11.0 will now make the array and function expression "hug", which decreases the indentation level: + + +```js +define([ + "jquery", + "common/global.context", + "common/log.event", + "some_project/square", + "some_project/rectangle", + "some_project/triangle", + "some_project/circle", + "some_project/star" +], function( + $, + Context, + EventLogger, + Square, + Rectangle, + Triangle, + Circle, + Star +) { + console.log("some code"); +}); +``` + +#### Format QUnit's `skip` like `test` ([#3735] by [@tmquinn]) + +Prettier was special-casing formatting for `describe`, `it`, and `test` to improve formatting in tests based on user expectations and requests. However, we were not formatting QUnit's `skip` method using the same logic, which led to mixed formatting when switching a test between `test` and `skip`: + + +```js +// Unformatted code +test("this is a really long description of some test I want to go here", function (assert) { + assert("something"); +}); + +skip("this is a really long description of some test I want to go here", function (assert) { + assert("something"); +}); + +// Formatted code (Prettier 1.10.2): +test("this is a really long description of some test I want to go here", function(assert) { + assert("something"); +}); + +skip( + "this is a really long description of some test I want to go here", + function(assert) { + assert("something"); + } +); +``` + +We now treat `skip` the same as `describe`, `it`, and `test`, which makes QUnit tests look more uniform: + + +```js +// Formatted code (Prettier 1.11.0): +test("this is a really long description of some test I want to go here", function(assert) { + assert("something"); +}); + +skip("this is a really long description of some test I want to go here", function(assert) { + assert("something"); +}); +``` + +#### Break JSX element if a string literal prop has a newline ([#4011] by [@duailibe]) + +Newline characters are allowed within a JSX string literal according to the JSX spec, but Prettier was not treating a JSX element as multi-line when one of its attribute values was a string containing a newline character. Prettier will now treat these elements as multi-line as expected: + + +```jsx +// Unformatted code: +; + +// Formatted code (Prettier 1.10.2): +; + +// Formatted code (Prettier 1.11.0): +; +``` + +#### Support JSX children spread with flow parser ([#3899] by [@vjeux]) + +Using a spread operator in a JSX children expression container position caused a parsing error in Prettier 1.10.2 when using the flow parser: + + +```jsx + + {...edges} + +``` + + +```jsx +SyntaxError: Unexpected token ... (2:4) + 1 | +> 2 | {...edges} + | ^^^ + 3 | +``` + +In Prettier 1.11.0, this syntax is supported with the flow parser. + +#### Fix error when printing flow maybe object return type ([#3948] by [@azz]) + +In Prettier 1.10.2, this code caused an error to be thrown: + + +```ts +function getScaledData({x}): ?{foo: number} {} +``` + +It has been fixed in Prettier 1.11.0. + +#### `--insert-pragma` no longer removes duplicate pragmas ([#3947] by [@azz]) + +Using `--insert-pragma` on code with duplicate pragmas was removing all but one of the duplicates. This has been fixed: + + +```js +// Unformatted code: +/** + * @x 1 + * @x 2 + */ + +// Formatted code (Prettier 1.10.2): +/** + * @format + * @x 2 + */ + +// Formatted code (Prettier 1.11.0): +/** + * @format + * @x 1 + * @x 2 + */ +``` + +#### Wrap flow-typecasted ternaries in parens ([#3940] by [@j-f1]) + +To improve code clarity, typecast ternaries will now be wrapped in parens: + + +```js +// Unformatted code: +const foo = ((1?2:3): number); + +// Formatted code (Prettier 1.10.2): +const foo = (1 ? 2 : 3: number); + +// Formatted code (Prettier 1.11.0): +const foo = ((1 ? 2 : 3): number); +``` + +#### Fix cases with missing semi when printing DeclareExportDeclaration ([#3979] by [@existentialism]) + +Semicolons were being mistakenly removed from `declare export default` statements in flow library definition files. This has been fixed. + +### TypeScript + +#### Don't remove parens from `(void 0)!` ([#3989] by [@azz]) + +Prettier was mistakenly removing parens from `(void 0)!`, which changed the AST of the program. This has been fixed. + +#### Properly chain when there is a TSNonNullExpression ([#4005] by [@ericanderson]) + +Prettier was not using its chaining formatting style when the chain included a TypeScript `!` non-null expression. This has been fixed: + + +```ts +// Unformatted code: +this.tokenLoads.get(locator).get(start).push({}); + +this.tokenLoads.get(locator)!.get(start).push({}); + +// Formatted code (Prettier 1.10.2): +this.tokenLoads + .get(locator) + .get(start) + .push({}); + +this.tokenLoads.get(locator)!.get(start).push({}); + +// Formatted code (Prettier 1.11.0): +this.tokenLoads + .get(locator) + .get(start) + .push({}); + +this.tokenLoads + .get(locator)! + .get(start) + .push({}); +``` + +### Markdown + +#### Heading after list item gets merged into list ([#3780] by [@ikatyang]) + +Prettier was mistakenly merging a heading beneath a list item into the list. Now it is treated as a header: + + +```md + +* Something +### Some heading + + +* Something + ### Some heading + + +* Something + +### Some heading +``` + +#### Don't parse HTML entities in link URLs ([#3780] by [@ikatyang]) + +Links whose URL contained HTML entities were mistakenly being changed when run through Prettier. They are now preserved. + + +```md + +[Test](http://localhost:8080/test?language=DE¤cy=EUR) + + +[Test](http://localhost:8080/test?language=DE¤cy=EUR) + + +[Test](http://localhost:8080/test?language=DE¤cy=EUR) +``` + +#### Fix indentation of template strings within code blocks ([#3676] by [@ikatyang]) + +Indentation of JS tagged template string content within markdown code blocks was being calculated incorrectly: + + +```md + +- 1 + - 2 + - 3 + ```js + something` + asd + + asd + + asd + ` + ``` + + +* 1 + + * 2 + + * 3 + + ```js + something` +asd + +asd + +asd +`; + ``` +``` + +It has been fixed in Prettier 1.11.0: + + +```md + +* 1 + + * 2 + + * 3 + + ```js + something` + asd + + asd + + asd + `; + ``` +``` + +#### Print multiline content correctly in footnoteDefinition ([#3787] by [@ikatyang]) + +Prettier was mistakenly using the incorrect level of indentation when printing a footnote that contained a code block. This has been fixed: + + +``````md + +Some text.[^fn] + +[^fn]: Here is a footnote which includes code. + + ```rs + fn main() { + println!("this is some Rust!"); + } + ``` + + +Some text.[^fn] + +[^fn]: Here is a footnote which includes code. + +```rs +fn main() { + println!("this is some Rust!"); +} +``` + + +Some text.[^fn] + +[^fn]: Here is a footnote which includes code. + + ```rs + fn main() { + println!("this is some Rust!"); + } + ``` +`````` + +#### Respect `tabWidth` for list items ([#3990] by [@ikatyang]) + +We've discovered that CommonMark diverges from the original Markdown syntax for code blocks in list items. This leads to scenarios where indented code-blocks in Markdown where being rendered as regular paragraphs. This change uses the `tabWidth` option to determine how much indentation to use in list items. Have a read through [#3459] if you're interested in the details. + + +```md + +1. Change to your home directory: + + cd + + +1. Change to your home directory: + + cd +``` + +This change should also improve indentation in text editors: + +![](/blog/assets/markdown-list-indent.gif) + +### API + +#### Support options in plugins ([#3775] by [@ikatyang]) + +As part of fleshing out our beta plugin API for community-made language plugins, we have added support for plugins to define their own options! + +Plugin options can now be printed in help: + +```sh +prettier --plugin @prettier/plugin-python --help +``` + +Likewise, plugin options can be specified on the command-line: + +```sh +prettier --plugin my-cool-prettier-plugin --my-custom-option +``` + +And plugin options can also be defined in `.prettierrc`: + +```json +{ "plugins": ["foo-plugin"], "fooOption": "bar" } +``` + +#### Don't allow kebab-cased options in config files ([#3622] by [@ikatyang]) + +Prettier was mistakenly allowing kebab-case variations of options in `.prettierrc` files: + +```json +{ "print-width": 100 } +``` + +This was not intended to be allowed, and has been reverted (in this case, you would need to use `printWidth`). + +### Configuration + +#### Respect EditorConfig when `--config` is passed ([#3992] by [@josephfrazier]) + +This is a minor change to the the way `.editorignore` files are handled. Previously when `--config` was provided, `.editorconfig` was ignored. + +--- + +Thanks to all of Prettier's contributors and the community for making Prettier a successful and productive open-source project! We couldn't do it without everyone ❤️ + +[@evilebottnawi]: https://github.com/evilebottnawi +[@ikatyang]: https://github.com/ikatyang +[@cryrivers]: https://github.com/Cryrivers +[@hudochenkov]: https://github.com/hudochenkov +[@tmquinn]: https://github.com/tmquinn +[@duailibe]: https://github.com/duailibe +[@vjeux]: https://github.com/vjeux +[@azz]: https://github.com/azz +[@j-f1]: https://github.com/j-f1 +[@existentialism]: https://github.com/existentialism +[@ericanderson]: https://github.com/ericanderson +[@josephfrazier]: https://github.com/josephfrazier +[@salemhilal]: https://github.com/salemhilal +[#3459]: https://github.com/prettier/prettier/issues/3459 +[#3828]: https://github.com/prettier/prettier/pull/3828 +[#3959]: https://github.com/prettier/prettier/pull/3959 +[#3802]: https://github.com/prettier/prettier/pull/3802 +[#4020]: https://github.com/prettier/prettier/pull/4020 +[#3967]: https://github.com/prettier/prettier/pull/3967 +[#4006]: https://github.com/prettier/prettier/pull/4006 +[#3745]: https://github.com/prettier/prettier/pull/3745 +[#3930]: https://github.com/prettier/prettier/pull/3930 +[#3906]: https://github.com/prettier/prettier/pull/3906 +[#3930]: https://github.com/prettier/prettier/pull/3930 +[#3814]: https://github.com/prettier/prettier/pull/3814 +[#3909]: https://github.com/prettier/prettier/pull/3909 +[#3770]: https://github.com/prettier/prettier/pull/3770 +[#3724]: https://github.com/prettier/prettier/pull/3724 +[#3769]: https://github.com/prettier/prettier/pull/3769 +[#3841]: https://github.com/prettier/prettier/pull/3841 +[#3723]: https://github.com/prettier/prettier/pull/3723 +[#3801]: https://github.com/prettier/prettier/pull/3801 +[#3738]: https://github.com/prettier/prettier/pull/3738 +[#3768]: https://github.com/prettier/prettier/pull/3768 +[#3792]: https://github.com/prettier/prettier/pull/3792 +[#3975]: https://github.com/prettier/prettier/pull/3975 +[#3665]: https://github.com/prettier/prettier/pull/3665 +[#3676]: https://github.com/prettier/prettier/pull/3676 +[#3735]: https://github.com/prettier/prettier/pull/3735 +[#4011]: https://github.com/prettier/prettier/pull/4011 +[#3899]: https://github.com/prettier/prettier/pull/3899 +[#3948]: https://github.com/prettier/prettier/pull/3948 +[#3947]: https://github.com/prettier/prettier/pull/3947 +[#3940]: https://github.com/prettier/prettier/pull/3940 +[#3979]: https://github.com/prettier/prettier/pull/3979 +[#3989]: https://github.com/prettier/prettier/pull/3989 +[#4005]: https://github.com/prettier/prettier/pull/4005 +[#3780]: https://github.com/prettier/prettier/pull/3780 +[#3780]: https://github.com/prettier/prettier/pull/3780 +[#3676]: https://github.com/prettier/prettier/pull/3676 +[#3787]: https://github.com/prettier/prettier/pull/3787 +[#3775]: https://github.com/prettier/prettier/pull/3775 +[#3622]: https://github.com/prettier/prettier/pull/3622 +[#3990]: https://github.com/prettier/prettier/pull/3990 +[#3992]: https://github.com/prettier/prettier/pull/3992 +[#4028]: https://github.com/prettier/prettier/pull/4028 +[#3830]: https://github.com/prettier/prettier/pull/3830 +[#4048]: https://github.com/prettier/prettier/pull/4048 diff --git a/website/blog/2018-04-09-plugin-php-0.1.md b/website/blog/2018-04-09-plugin-php-0.1.md new file mode 100644 index 00000000..4b8491cd --- /dev/null +++ b/website/blog/2018-04-09-plugin-php-0.1.md @@ -0,0 +1,50 @@ +--- +author: Christian Zosel (@chrzosel) +authorURL: https://twitter.com/chrzosel +title: Prettier for PHP 0.1: First alpha release 🎉 +--- + +After more than 200 merged pull requests since mid December 2017, we're happy to announce the first alpha release of [Prettier for PHP](https://github.com/prettier/plugin-php). In this blog post, we'd like to give a short overview of how the plugin works, its philosophy, and what to expect in the future. + + + +## How does it work? + +Adding support for a new language to Prettier requires two things: + +* A parser, which turns your source code into an abstract syntax tree (AST). Since Prettier is written in JavaScript, we're using [php-parser](https://github.com/glayzzle/php-parser), which is a PHP parser written in JS. + +* A printer, that turns the AST into a special format called `doc` that Prettier requires to do its magic. It is basically your source code plus some information about where linebreaks can be added, how it should be indented, and so on. + +While we could benefit from existing work on the parsing side, the printer had to be developed from scratch to support all of PHP's various AST node types. Our first alpha release today means that we are confident that the printer can re-print your code without breaking it. + +If you'd like to read more about the plugin API, see [the docs](https://prettier.io/docs/en/plugins.html). + +## Philosophy + +When building a code formatter, it can be tempting to add many options to account for all the different code styles there are. The PHP plugin sticks to Prettier's opinionated philosophy and doesn't add any new options. Instead, we identified [PSR-1, PSR-2 and PSR-12](https://www.php-fig.org/psr/) as one of the most popular coding styles in the PHP community, and decided to use this as our guide when it came to formatting our re-printed PHP code. + +From the experience gained with Prettier for JavaScript, we believe that offering as few configuration options as possible is one of Prettier's biggest strengths. After our first stable release, we will consider adding a very limited set of options for the most controversial styling decisions (e.g. [brace style](https://github.com/prettier/plugin-php/issues/107)), but remain conservative about it. + +## What's next + +For our first stable release, the following milestones still have to be reached: + +1. 100% safety that we're not breaking code during formatting. + + We're quite close to this goal already today. + +2. Pretty output + + The plugin doesn't deserve its name if the output isn't pretty. While it is already quite good today, handling all the edge cases will require some more work. + +3. Integrations + + Today, you can try the plugin from the command line by following the [instructions in the README](https://github.com/prettier/plugin-php#install). Plugin support in your favorite editor might not work yet, though (see the related issues for [VS Code](https://github.com/prettier/prettier-vscode/issues/395), [Atom](https://github.com/prettier/prettier-atom/issues/395) and [Vim](https://github.com/prettier/vim-prettier/issues/119)). Also, the PHP plugin is not integrated in Prettier's [playground](https://prettier.io/playground/) yet. + +Are you excited about Prettier for PHP? Then we'd love you to help us reach our first stable release by + +* Trying it on your code base +* [Reporting any issues](https://github.com/prettier/plugin-php/issues) you find with it +* Contributing ([how to get started](https://github.com/prettier/plugin-php/blob/master/CONTRIBUTING.md)) +* Spreading the word 😉 diff --git a/website/blog/assets/github-diff-ternary-in-jsx.png b/website/blog/assets/github-diff-ternary-in-jsx.png new file mode 100644 index 00000000..77881946 Binary files /dev/null and b/website/blog/assets/github-diff-ternary-in-jsx.png differ diff --git a/website/blog/assets/markdown-list-indent.gif b/website/blog/assets/markdown-list-indent.gif new file mode 100644 index 00000000..2bd06bc3 Binary files /dev/null and b/website/blog/assets/markdown-list-indent.gif differ diff --git a/website/blog/assets/markdown-lists.gif b/website/blog/assets/markdown-lists.gif new file mode 100644 index 00000000..d9b78e58 Binary files /dev/null and b/website/blog/assets/markdown-lists.gif differ diff --git a/website/blog/assets/markdown-tables.gif b/website/blog/assets/markdown-tables.gif new file mode 100644 index 00000000..3d5296a9 Binary files /dev/null and b/website/blog/assets/markdown-tables.gif differ diff --git a/website/blog/assets/prettier-revolution-conf.png b/website/blog/assets/prettier-revolution-conf.png new file mode 100644 index 00000000..c626b6b2 Binary files /dev/null and b/website/blog/assets/prettier-revolution-conf.png differ diff --git a/website/core/Footer.js b/website/core/Footer.js index 46e609e3..f8b83699 100644 --- a/website/core/Footer.js +++ b/website/core/Footer.js @@ -7,9 +7,7 @@ const GithubButton = props => ( className="github-button" href={props.config.githubUrl} data-icon="octicon-star" - data-count-href={`/${props.config.repo}/stargazers`} - data-count-api={`/repos/${props.config.repo}#stargazers_count`} - data-count-aria-label="# stargazers on GitHub" + data-show-count="true" aria-label="Star this project on GitHub" > Star @@ -22,13 +20,18 @@ GithubButton.propTypes = { class Footer extends React.Component { url(path) { - return this.props.config.baseUrl + "docs/" + this.props.language + path; + const language = this.props.language || "en"; + return `${this.props.config.baseUrl}docs/${language}${path}`; + } + + usersUrl() { + const language = this.props.language || "en"; + return `${this.props.config.baseUrl}${language}/users`; } render() { - // const currentYear = new Date().getFullYear(); return ( -