Skip to content

Commit

Permalink
Add option to require @prettier or @Format pragma (prettier#2772)
Browse files Browse the repository at this point in the history
* Add option to require @prettier or @Format pragma

Fixes prettier#2397.

Inspired by `eslint-plugin-prettier` and the discussion in prettier#2397, this
implements requiring a special comment pragma to be present in a file's
first comment in order to be formatted.

This will help large codebases gradually transition to prettier over
time without tons of churn or large code reviews.

I implemented this as a standard prettier "option", not just a typical
`argv` flag, as it is relevant in both the cli and the api. This way it
can be provided programmatically, on the command line, or standardized
in a prettierrc file so like the style options, every user can use this
setting consistently and only apply prettier to relevant files, no
mattier their editor integration.

This requires the pragma begin with `@` (in fact it's inserted if the
user doesn't provide it). Currently the usage implies it must be
"prettier" or "format", but it can technically be any value other than
"none", which is similar to the `trailingCommas` option.

cc @vjeux

* Don't quote anything in runPrettier; this is usually handled by a shell

* Make --require-pragma a boolean option

* Use jest-docblock to find pragmas without parsing the ast

* Clarify docs

* includes -> indexOf

* Move test out of integration
  • Loading branch information
wbinnssmith authored and vjeux committed Sep 13, 2017
1 parent fd937ec commit d5e5d66
Show file tree
Hide file tree
Showing 13 changed files with 145 additions and 2 deletions.
13 changes: 12 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,17 @@ you can pass `--no-config` instead.

Path to a file containing patterns that describe files to ignore. By default, prettier looks for `./.prettierignore`.

#### `--require-pragma`

Require a special comment, called a pragma, to be present in the file's first docblock comment in order for prettier to format it.
```js
/**
* @prettier
*/
```

Valid pragmas are `@prettier` and `@format`.

#### `--list-different`

Another useful flag is `--list-different` (or `-l`) which prints the filenames of files that are different from Prettier formatting. If there are differences the script errors out, which is useful in a CI scenario.
Expand All @@ -288,7 +299,7 @@ Do not look for a configuration file. The default settings will be used.

#### `--config-precedence`

Defines how config file should be evaluated in combination of CLI options.
Defines how config file should be evaluated in combination of CLI options.

**cli-override (default)**

Expand Down
23 changes: 23 additions & 0 deletions docs/options.md
Original file line number Diff line number Diff line change
Expand Up @@ -133,3 +133,26 @@ Default | CLI Override | API Override
--------|--------------|-------------
None | `--stdin-filepath <string>` | `filepath: "<string>"`

## Require pragma
Prettier can restrict itself to only format files that contain a special comment, called a pragma, at the top of the file. This is very useful
when gradually transitioning large, unformatted codebases to prettier.

For example, a file with the following as its first comment will be formatted when `--require-pragma` is supplied:

```js
/**
* @prettier
*/
```

or

```js
/**
* @format
*/
```

Default | CLI Override | API Override
--------|--------------|-------------
None | `--require-pragma` | `requirePragma`
10 changes: 10 additions & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ const normalizeOptions = require("./src/options").normalize;
const parser = require("./src/parser");
const printDocToDebug = require("./src/doc-debug").printDocToDebug;
const config = require("./src/resolve-config");
const docblock = require("jest-docblock");

function guessLineEnding(text) {
const index = text.indexOf("\n");
Expand All @@ -30,6 +31,11 @@ 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;
Expand All @@ -56,6 +62,10 @@ function ensureAllCommentsPrinted(astComments) {
}

function formatWithCursor(text, opts, addAlignmentSize) {
if (opts.requirePragma && !hasPragma(text)) {
return { formatted: text };
}

text = stripBom(text);
addAlignmentSize = addAlignmentSize || 0;

Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
"globby": "^6.1.0",
"graphql": "0.10.1",
"ignore": "^3.3.3",
"jest-docblock": "^21.0.2",
"jest-validate": "20.0.3",
"minimatch": "3.0.4",
"minimist": "1.2.0",
Expand Down
5 changes: 4 additions & 1 deletion src/cli-constant.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const booleanOptionNames = [
"bracket-spacing",
"jsx-bracket-same-line",
// Deprecated in 0.0.10
"flow-parser"
"flow-parser",
"require-pragma"
];

const stringOptionNames = [
Expand Down Expand Up @@ -86,6 +87,8 @@ Available options:
Finds and prints the path to a configuration file for a given input file.
--ignore-path <path> Path to a file containing patterns that describe files to ignore.
Defaults to ./.prettierignore.
--require-pragma
Require either '@prettier' or '@format' to be present in the file's first docblock comment in order for it to be formatted.
--stdin Read input from stdin.
--stdin-filepath Path to the file used to read from stdin.
--print-width <int> Specify the length of line that the printer will wrap on. Defaults to 80.
Expand Down
1 change: 1 addition & 0 deletions src/cli-util.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ function getOptions(argv) {
printWidth: getIntOption(argv, "print-width"),
tabWidth: getIntOption(argv, "tab-width"),
bracketSpacing: argv["bracket-spacing"],
requirePragma: argv["require-pragma"],
singleQuote: argv["single-quote"],
jsxBracketSameLine: argv["jsx-bracket-same-line"],
filepath: argv["stdin-filepath"],
Expand Down
1 change: 1 addition & 0 deletions src/options.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ const defaults = {
bracketSpacing: true,
jsxBracketSameLine: false,
parser: "babylon",
requirePragma: false,
semi: true
};

Expand Down
59 changes: 59 additions & 0 deletions tests/require-pragma/__snapshots__/jsfmt.spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`module-with-pragma.js 1`] = `
/**
* @flow
* @format
*/
function foo(bar)
{
return bar +
3 +
4;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* @flow
* @format
*/
function foo(bar) {
return bar + 3 + 4;
}
`;

exports[`module-without-pragma.js 1`] = `
/**
* @flow
*/
function foo(bar)
{
return bar +
3 +
4;
}
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/**
* @flow
*/
function foo(bar)
{
return bar +
3 +
4;
}
`;
1 change: 1 addition & 0 deletions tests/require-pragma/jsfmt.spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
run_spec(__dirname, { requirePragma: true });
14 changes: 14 additions & 0 deletions tests/require-pragma/module-with-pragma.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/**
* @flow
* @format
*/

function foo(bar)


{

return bar +
3 +
4;
}
13 changes: 13 additions & 0 deletions tests/require-pragma/module-without-pragma.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/**
* @flow
*/

function foo(bar)


{

return bar +
3 +
4;
}
2 changes: 2 additions & 0 deletions tests_integration/__tests__/__snapshots__/early-exit.js.snap
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ Available options:
Finds and prints the path to a configuration file for a given input file.
--ignore-path <path> Path to a file containing patterns that describe files to ignore.
Defaults to ./.prettierignore.
--require-pragma
Require either '@prettier' or '@format' to be present in the file's first docblock comment in order for it to be formatted.
--stdin Read input from stdin.
--stdin-filepath Path to the file used to read from stdin.
--print-width <int> Specify the length of line that the printer will wrap on. Defaults to 80.
Expand Down
4 changes: 4 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -2258,6 +2258,10 @@ jest-docblock@^20.0.1, jest-docblock@^20.0.3:
version "20.0.3"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-20.0.3.tgz#17bea984342cc33d83c50fbe1545ea0efaa44712"

jest-docblock@^21.0.2:
version "21.0.2"
resolved "https://registry.yarnpkg.com/jest-docblock/-/jest-docblock-21.0.2.tgz#66f69ddb440799fc32f91d0ac3d8d35e99e2032f"

jest-environment-jsdom@^20.0.3:
version "20.0.3"
resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-20.0.3.tgz#048a8ac12ee225f7190417713834bb999787de99"
Expand Down

0 comments on commit d5e5d66

Please sign in to comment.