Merge branch 'main' into next

# Conflicts:
#	package.json
#	tests/css/atrule/each.css
#	tests/css/atrule/for.css
#	tests/css/atrule/if-else.css
#	tests/css/atrule/include.css
#	tests/css/atrule/while.css
#	tests/format/css/atrule/each.css
#	tests/format/css/atrule/for.css
#	tests/format/css/atrule/if-else.css
#	tests/format/css/atrule/include.css
#	tests/format/css/atrule/while.css
#	tests/scss/atrule/each.scss
#	tests/scss/atrule/for.scss
#	tests/scss/atrule/if-else.scss
#	tests/scss/atrule/include.scss
#	tests/scss/atrule/while.scss
#	yarn.lock
This commit is contained in:
fisker Cheung committed 2021-05-06 16:00:12 +08:00
commit 60619338f1
6814 files changed
+4413 -1515

No files matched your search

+3 -3
View File
@@ -1,6 +1,7 @@
!/*.js
/tests/**/*.js
!/tests/**/jsfmt.spec.js
/tests/format/**/*.js
/tests/integration/cli/
!/tests/format/**/jsfmt.spec.js
!/**/.eslintrc.js
/test*.js
/scripts/release/node_modules
@@ -10,4 +11,3 @@
/website/build/
/website/static/playground.js
/website/static/lib/
/tests_integration/cli/
+4 -1
View File
@@ -91,7 +91,9 @@ overrides:
parserOptions:
sourceType: module
- files:
- "{tests,tests_config,tests_integration}/**/*.js"
- "tests/format/**/jsfmt.spec.js"
- "tests/config/**/*.js"
- "tests/integration/**/*.js"
env:
jest: true
plugins:
@@ -120,6 +122,7 @@ overrides:
rules:
prettier-internal-rules/consistent-negative-index-access: error
prettier-internal-rules/flat-ast-path-call: error
prettier-internal-rules/no-conflicting-comment-check-flags: error
prettier-internal-rules/no-doc-builder-concat: error
prettier-internal-rules/no-empty-flat-contents-for-if-break: error
prettier-internal-rules/no-unnecessary-ast-path-call: error
+3
View File
@@ -28,6 +28,9 @@ f56d620be529b60c13032681446c1eb76e0fb088
# 2.0.0
9dad95b35f935edce4c3d6cfa45c79a0b9c82b9f
# Restructure test files (#10415)
ece93681f1010796e7d8eb4394196ccaef0cbc9c
# Categorize tests (#8239 #8248 #8249 #8251)
b585bd6fa4d750a98e277303c428edfc48fea3f4
f8c5b1fd1da4d67bc09d12bc3411b70d0fa4f4a1
+7
View File
@@ -15,6 +15,13 @@ updates:
day: sunday
time: "01:00"
- package-ecosystem: "npm"
directory: "/scripts/release"
schedule:
interval: weekly
day: sunday
time: "01:00"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
+1 -1
View File
@@ -42,7 +42,7 @@ jobs:
- name: Install Client Package
if: matrix.NPM_CLIENT == 'pnpm'
run: npm install --global ${{ matrix.NPM_CLIENT }}
run: npm install --global pnpm@5
- name: Run Tests
run: yarn test:dev-package --maxWorkers=2
+9 -4
View File
@@ -19,21 +19,26 @@ jobs:
- "macos-latest"
- "windows-latest"
node:
- "16"
- "14"
- "12"
- "10"
include:
# only enable coverage on the fastest job
- os: "ubuntu-latest"
node: "14"
node: "16"
ENABLE_CODE_COVERAGE: true
FULL_TEST: true
CHECK_TEST_PARSERS: true
- os: "ubuntu-latest"
node: "15"
exclude:
- os: "macos-latest"
node: "14"
- os: "macos-latest"
node: "12"
- os: "windows-latest"
node: "14"
- os: "windows-latest"
node: "12"
env:
@@ -66,19 +71,19 @@ jobs:
run: yarn test --maxWorkers=2
- name: Upload Coverage
uses: codecov/codecov-action@v1.3.1
uses: codecov/codecov-action@v1.4.1
if: matrix.ENABLE_CODE_COVERAGE
with:
fail_ci_if_error: true
# #8073 test
- name: Run Tests (PRETTIER_FALLBACK_RESOLVE)
run: yarn test "tests_integration/__tests__/(config|plugin)"
run: yarn test "tests/integration/__tests__/(config|plugin)"
env:
PRETTIER_FALLBACK_RESOLVE: true
- name: Upload Coverage (PRETTIER_FALLBACK_RESOLVE)
uses: codecov/codecov-action@v1.3.1
uses: codecov/codecov-action@v1.4.1
if: matrix.ENABLE_CODE_COVERAGE
with:
fail_ci_if_error: true
+8 -3
View File
@@ -24,7 +24,7 @@ jobs:
- name: Cache Build Results
id: build-cache
uses: actions/cache@v2.1.4
uses: actions/cache@v2.1.5
with:
path: .cache
key: v2-build-cache-${{ hashFiles('yarn.lock') }}-${{ hashFiles('scripts/build/**/*') }}-${{ github.ref }}-
@@ -85,18 +85,23 @@ jobs:
- "macos-latest"
- "windows-latest"
node:
- "16"
- "14"
- "12"
- "10"
include:
- os: "ubuntu-latest"
node: "14"
node: "16"
FULL_TEST: true
- os: "ubuntu-latest"
node: "15"
exclude:
- os: "macos-latest"
node: "14"
- os: "macos-latest"
node: "12"
- os: "windows-latest"
node: "14"
- os: "windows-latest"
node: "12"
env:
@@ -140,7 +145,7 @@ jobs:
# #8073 test
- name: Run Tests (PRETTIER_FALLBACK_RESOLVE)
run: yarn test "tests_integration/__tests__/(config|plugin)"
run: yarn test "tests/integration/__tests__/(config|plugin)"
env:
NODE_ENV: production
PRETTIER_FALLBACK_RESOLVE: true
+5 -5
View File
@@ -1,11 +1,11 @@
dist/
.cache/
coverage/
/tests/**/*.*
!/tests/**/jsfmt.spec.js
/tests_integration/cli/
/tests_integration/plugins/
/tests_integration/custom-parsers/
/tests/format/**/*.*
!/tests/format/**/jsfmt.spec.js
/tests/integration/cli/
/tests/integration/plugins/
/tests/integration/custom-parsers/
/website/build/
/website/static/lib/
/website/static/playground.js
+1 -1
View File
@@ -51,7 +51,7 @@ Parameters:
- **`parsers`**: A list of parser names. The tests verify that the parsers in this list produce the same output. If the list includes `typescript`, then `babel-ts` is included implicitly. If the list includes `babel`, and the current directory is inside `tests/js`, then `espree` and `meriyah` are included implicitly.
- **`options`**: In addition to Prettier's formatting option, can contain the `errors` property to specify that it's expected that the formatting shouldn't be successful and an error should be thrown for all (`errors: true`) or some combinations of input entries and parsers.
The implementation of `run_spec` can be found in [`tests_config/run_spec.js`](tests_config/run_spec.js).
The implementation of `run_spec` can be found in [`tests/config/format-test.js`](tests/config/format-test.js).
`tests/flow-repo/` contains the Flow test suite and is not supposed to be edited by hand. To update it, clone the Flow repo next to the Prettier repo and run: `node scripts/sync-flow-tests.js ../flow/tests/`.
+1 -1
View File
@@ -1,4 +1,4 @@
#### Fix `lineSuffixBoundary` (#10122 by @thorn0)
#### Fix `lineSuffixBoundary` IR command (#10122 by @thorn0)
There was a bug in the implementation of the [`lineSuffixBoundary`][lsb] command that significantly limited its usefulness: the printer algorithm didn't correctly consider it a potential line break. Now that the bug has been fixed, we urge plugin authors to give this command another try and see if it can help them simplify printing of trailing comments.
+2
View File
@@ -33,3 +33,5 @@ To print a sub-property of the current node, use `["property1", "property2"]`:
+ return print(["child", "child"]);
}
```
See also an example in the [docs](https://prettier.io/docs/en/plugins.html#print).
+2 -2
View File
@@ -1,3 +1,3 @@
#### Treat `.prettierrc` as `yaml` (#8105 by @fisker)
#### Treat `.prettierrc` as YAML when formatting it (#8105 by @fisker)
`.prettierrc` file can written in both JSON and YAML, previously we infer the parser to be `json`, if it's write in YAML, there will be a `SyntaxError` thrown when formatting it, now it's treat as a YAML file, but if it's write in JSON, it won't format as a normal YAML file, it will be formatted as JSON.
The `.prettierrc` file can be written in either JSON or YAML. Previously, when Prettier formatted it, the parser was inferred to be `json`, which lead to a `SyntaxError` thrown if the content was YAML. Now it’s treated as a YAML file. However, if it's JSON, it will be formatted as JSON (not as JSON-like YAML).
+3 -3
View File
@@ -1,15 +1,15 @@
#### Add CLI option to prevent errors on unmatched pattern (#10058 by @daronmcintosh)
The following command will not throw an error when it doesn't find a YAML file.
The Prettier CLI will no longer display an error when no files match the glob pattern passed as input.
```sh
// Prettier stable
# Prettier stable
$ npx prettier --check "prettier/docs/*.yaml"
Checking formatting...
[error] No files matching the pattern were found: "prettier/docs/*.yaml".
All matched files use Prettier code style!
// Prettier main
# Prettier main
$ npx prettier --check --no-error-on-unmatched-pattern "prettier/docs/*.yaml"
Checking formatting...
All matched files use Prettier code style!
+1 -1
View File
@@ -10,7 +10,7 @@ tests\flow-repo\config_module_system_node_resolve_dirname\custom_resolve_dir\tes
tests\flow-repo\config_module_system_node_resolve_dirname\subdir\custom_resolve_
All matched files use Prettier code style!
// Prettier main
# Prettier main
$ prettier tests/flow-repo/config_module_system_node_resolve_dirname --check
Checking formatting...
All matched files use Prettier code style!
+34
View File
@@ -0,0 +1,34 @@
#### Exclude `@keyframes` params from being parsed as a Less variable (#10773 by @tmazeika)
<!-- prettier-ignore -->
```css
/* Input */
@keyframes :global(spin) {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
/* Prettier stable */
@keyframes: global(spin){
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
};
/* Prettier main */
@keyframes :global(spin) {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
```
+2 -1
View File
@@ -1,9 +1,10 @@
#### Fix absolute path in custom CSS -custom-url() calls (#9966 by @vjeux)
#### Fix absolute path in custom CSS `-custom-url()` calls (#9966 by @vjeux)
The CSS parser is parsing this as `["division", "absolute/path"]` instead of a single `"/absolute/path"` token unless you are in a `url()` call. Because we put space after division, it results in an incorrect path. The fix was to avoid printing a space if a division is the first token of a call, which hopefully should be safe.
<!-- prettier-ignore -->
```css
/* Input */
-custom-url(/absolute/path)
/* Prettier stable */
-2
View File
@@ -1,7 +1,5 @@
#### Support for Flow's indexed access type (#10594 by @gkz)
Support for Flow's indexed access type.
<!-- prettier-ignore -->
```js
// Input
+1 -1
View File
@@ -1,4 +1,4 @@
#### Fix missing semi in `declare export * from …` (#9767 by @fisker)
#### Fix missing semicolon in `declare export * from …` (#9767 by @fisker)
<!-- Optional description if it makes sense. -->
+19
View File
@@ -0,0 +1,19 @@
#### Fix missing space after keyword in anonymous operations (#10689 by @patriscus)
<!-- prettier-ignore -->
```graphql
# Input
query ($unnamed: String) {
id
}
# Prettier stable
query($unnamed: String) {
id
}
# Prettier main
query ($unnamed: String) {
id
}
```
+4 -4
View File
@@ -6,19 +6,19 @@
- implement simple formatting for the `class` attribute, like Prettier formatted it in HTML before v2.3.0
<!-- prettier-ignore -->
```html
// Input
```hbs
{{!-- Input --}}
<div class="
foo"></div>
<div bar='"{{expr}}"'></div>
<div baz="\{{ non-expression }}"></div>
// Prettier stable
{{!-- Prettier stable --}}
<div class="[object Object],foo"></div>
<div bar=""{{expr}}""></div>
<div baz="{{ non-expression }}"></div>
// Prettier main
{{!-- Prettier main --}}
<div class="foo"></div>
<div bar='"{{expr}}"'></div>
<div baz="\{{ non-expression }}"></div>
+11 -4
View File
@@ -1,16 +1,23 @@
#### Move Handlebars support out of alpha to release (#10290 by @dcyriller & @thorn0)
#### [HIGHLIGHT] Move Handlebars support out of alpha to release (#10290 by @dcyriller & @thorn0)
This started in 2017. Handlebars support has been in Prettier for a while, but it wasn’t released officially as it wasn’t really ready. Its status was “alpha”, “experimental”, “beta”. Then, if you checked older release notes, you could see that after “beta” somehow it was “alpha” again…
This started in 2017. Handlebars support has been in Prettier for a while, but it wasn’t released officially as it wasn’t really ready. Its status went from “alpha” to “experimental” to “beta” and then, if you checked older release notes, you can see that after “beta” somehow it was “alpha” again…
Well, anyway, it finally is happening: Prettier can now officially format Handlebars files! 🎉
Well, anyway, it finally is happening: Prettier can now officially format HTML templates with Handlebars! 🎉
It uses [Glimmer], [Ember]’s Handlebars parser, so it should be compliant with the HTML spec thanks to the Ember team.
The [`--html-whitespace-sensitivity`][hws-option] option is supported and defaults to `strict`, which means that Prettier will always respect the presence or absence of whitespace around tags and consider it unsafe to add whitespace where there were none and vice versa as this can affect how the document is rendered in the browser. The `css` value is not yet supported (treated as `strict` for now).
The feature is called “Ember / Handlebars” and not just “Handlebars” because Glimmer doesn’t support some syntax and use cases of Handlebars. This is mostly due to the fact that [Handlebars], being a template engine (a preprocessor), doesn’t care about the underlying syntax of the content it processes whereas Glimmer parses two syntaxes – HTML and Handlebars – at the same time and combines the result into a single tree, which Prettier can print. This means Prettier won’t format Handlebars files that can’t be parsed into such a tree, either because the underlying syntax isn’t HTML or because template directives and tags overlap in a way that can’t be represented in a tree (e.g., `{{#if foo}}<div>{{/if}`). Even with these restrictions, the formatter still seems to be useful enough to non-Ember Handlebars users.
The feature is called “Ember / Handlebars” and not just “Handlebars” because Glimmer doesn’t support some syntax and use cases of Handlebars. This is mostly due to the fact that [Handlebars], being a template engine (a preprocessor), doesn’t care about the underlying syntax of the content it processes whereas Glimmer parses two syntaxes – HTML and Handlebars – at the same time and combines the result into a single tree, which Prettier can print. This means Prettier won’t format Handlebars files that can’t be parsed into such a tree, either because the underlying syntax isn’t HTML or because template directives and tags overlap in a way that can’t be represented in a tree (e.g., `{{#if foo}}<div>{{/if}`). Even with these restrictions, the formatter still seems to be useful enough to non-Ember Handlebars users. As for the syntax unsupported by Ember, there is a good chance to see support for it in future versions of Prettier as Glimmer uses a full-fledged Handlebars parser under the hood.
Files with the extensions `.hbs` and `.handlebars` are recognized as Handlebars by default. For other extensions, the [`--parser`](parser-option) option with the value `glimmer` has to be specified – for example, using command line or, better yet, [configuration overrides].
See the formatter in action on the [playground][playground-glimmer]!
[glimmer]: https://www.npmjs.com/package/@glimmer/syntax
[ember]: https://emberjs.com
[hws-option]: https://prettier.io/docs/en/options.html#html-whitespace-sensitivity
[handlebars]: https://handlebarsjs.com/
[parser-option]: https://prettier.io/docs/en/options.html#parser
[configuration overrides]: https://prettier.io/docs/en/configuration.html#configuration-overrides
[playground-glimmer]: https://prettier.io/playground/#N4Igxg9gdgLgprEAuEACVAeAJgSwG7qFgA2AhgM7kC8AOiAjAE4CedhqAfDVOhgBYBGDsGDoYOGMTjsAvjIwB6QVx6ZcBdCQrU6AIwhZWIFe1OERhfYbOE53Reo6YFjkABoQEAA7jo5ZKCkjIwQAO4ACkEI-iikxKGkzP4euoykYADWcDAAyl7pOFAA5shMAK5wHnAAtrpwWFj1ADKkxWWkRXAAYhCM1aQw4sXIIKRlMBDuIHww1cQA6nwScOT5YHA50RL4Eswj5Ew4YDBTheRwjDDhaUX9yABmceceAFbkAB4AQmmZ2Tmk1TgTUKcAeT0qIDe7xyhSKUgAimUIPAwcRniB8oxzowRnCcNVATiPF5GIUYPMcFgYHxkAAOAAMxJC53maS8IxJKwueFBHgAjkj4NdvDFRuQALRQOD1epTRhwAU4eXXDp3JCPNEQ87VHClRgVDzkWEIwWg9XgjwwUi6ClUmlIABMlrSOGIsIAwhACaQRisAKxTMrnAAq1piGvReAqAEkoI1YDkwKSfABBOM5GDMKSo85yIA
+40
View File
@@ -0,0 +1,40 @@
#### Do not break opening mustache and path (#10586 by @dcyriller)
<!-- prettier-ignore -->
```handlebars
{{!-- Input --}}
<GlimmerComponent
@errors={{or this.aVeryLongProperty (and this.aProperty (v-get bike "number" "message"))}}
data-test-beneficiary-account-number
/>
<GlimmerComponent
@progress={{aPropertyEngdingAfterEightiethColumnToHighlightAWeirdClosingParenIssue}}
/>
{{!-- Prettier stable --}}
<GlimmerComponent
@errors={{
or
this.aVeryLongProperty
(and this.aProperty (v-get bike "number" "message"))
}}
data-test-beneficiary-account-number
/>
<GlimmerComponent
@progress={{
aPropertyEngdingAfterEightiethColumnToHighlightAWeirdClosingParenIssue
}}
/>
{{!-- Prettier main --}}
<GlimmerComponent
@errors={{or
this.aLongProperty
(and this.aProperty (v-get bike "number" "message"))
}}
data-test-beneficiary-account-number
/>
<GlimmerComponent
@progress={{aPropertyEngdingAfterEightiethColumnToHighlightAWeirdClosingParenIssue}}
/>
```
+2 -1
View File
@@ -1,7 +1,8 @@
#### Print final blockParam on its own line (#9978 by @dcyriller)
#### Print final `blockParam` on its own line (#9978 by @dcyriller)
<!-- prettier-ignore -->
```hbs
{{!-- Input --}}
<MyComponent @prop={{true}} @prop2={{true}} @prop3={{true}} @prop4={{true}} as |thing|></MyComponent>
{{#block param hashKey=hashValue hashKey=hashValue hashKey=hashValue as |blockParam|}}
Hello
+1 -1
View File
@@ -1,4 +1,4 @@
#### Prefix-based multiline formatting for the `class` attribute (#7865 by @thorn0)
#### [IMPROVEMENT] Prefix-based multiline formatting for the `class` attribute (#7865 by @thorn0)
Formatting of HTML class names will now keep classes on one line until the line length limit is reached; at that point, consecutive classes with the same prefix will be grouped together on each line. For layout frameworks such as Bootstrap and Tailwind CSS, which add many classes to an element, this is important for readability and maintainability vs. the previous behavior (keeping all classes on one line) or e.g. putting each class on its own line.
+1 -10
View File
@@ -1,15 +1,7 @@
#### Improve visual separation between header and body in classes with multiline headers (#10085 by @sosukesuzuki)
#### [IMPROVEMENT:2] Improve visual separation between header and body in classes with multiline headers (#10085 by @sosukesuzuki)
<!-- prettier-ignore -->
```ts
// Input
class loooooooooooooooooooong
extends looooooooooooooooooong
implements loooooooooooooooooooong
{
property: string;
}
// Prettier stable
class loooooooooooooooooooong
extends looooooooooooooooooong
@@ -24,5 +16,4 @@ class loooooooooooooooooooong
{
property: string;
}
```
+2 -4
View File
@@ -1,4 +1,4 @@
#### Concise formatting of number-only arrays (#10106, #10160 by @thorn0)
#### [IMPROVEMENT:3] Concise formatting of number-only arrays (#10106, #10160 by @thorn0)
While in general, Prettier avoids this kind of formatting because it's not diff-friendly, in this special case we decided that the benefits outweigh the risks.
@@ -26,11 +26,10 @@ const lazyCatererNumbers = [
22,
29,
37,
// ... ✂ a lot of lines ✂ ...
// ... ✂ 46 lines ✂ ...
1379,
];
// Prettier main
const lazyCatererNumbers = [
1, 2, 4, 7, 11, 16, 22, 29, 37, 46,
@@ -41,5 +40,4 @@ const lazyCatererNumbers = [
// n > 40
821, 862, 904, 947, 991, 1036, 1082, 1129, 1177, 1226, 1276, 1327, 1379,
];
```
-15
View File
@@ -1,15 +0,0 @@
#### Break assignment whose rhs is wrapped by an unary expression (#10158 by @sosukesuzuki)
<!-- prettier-ignore -->
```js
// Input
const loooooooooooooooooooooooooong = !looooooooooooooong.looooooooooooooong.loooooong;
// Prettier stable
const loooooooooooooooooooooooooong = !looooooooooooooong.looooooooooooooong
.loooooong;
// Prettier main
const loooooooooooooooooooooooooong =
!looooooooooooooong.looooooooooooooong.loooooong;
```
+7 -23
View File
@@ -1,43 +1,24 @@
#### Consistent indentations for conditional operators (#10187, #10266 by @sosukesuzuki)
#### [IMPROVEMENT:6] Consistent indentation for conditional operators (#10187, #10266 by @sosukesuzuki)
<!-- prettier-ignore -->
```ts
// Input
const dotNotationMemberExpression = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
).TSESTree.BinaryExpression;
const computedMemberExpression = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
)[TSESTree.BinaryExpression];
const callExpressionCallee = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
)(TSESTree.BinaryExpression);
const typeScriptAsExpression = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
) as TSESTree.BinaryExpression;
// Prettier stable
const dotNotationMemberExpression = (callNode.parent?.type ===
AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
).TSESTree.BinaryExpression;
const computedMemberExpression = (callNode.parent?.type ===
AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent)[TSESTree.BinaryExpression];
const callExpressionCallee = (callNode.parent?.type ===
AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent)(TSESTree.BinaryExpression);
const typeScriptAsExpression = (callNode.parent?.type ===
AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
@@ -49,16 +30,19 @@ const dotNotationMemberExpression = (
? callNode.parent.parent
: callNode.parent
).TSESTree.BinaryExpression;
const computedMemberExpression = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
)[TSESTree.BinaryExpression];
const callExpressionCallee = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
: callNode.parent
)(TSESTree.BinaryExpression);
const typeScriptAsExpression = (
callNode.parent?.type === AST_NODE_TYPES.ChainExpression
? callNode.parent.parent
+1 -17
View File
@@ -1,25 +1,9 @@
#### Format assignments more consistently (#10222 by @thorn0)
#### [HIGHLIGHT] Format assignments more consistently (#10222, #10643, #10672 by @thorn0; #10158 by @sosukesuzuki)
Previously, Prettier had a lot of trouble with figuring out how to break lines in assignments. E.g., long right-hand sides often stayed unbroken. Not anymore.
<!-- prettier-ignore -->
```ts
// Input
aParticularlyLongAndObnoxiousNameForIllustrativePurposes =
anotherVeryLongNameForIllustrativePurposes;
aParticularlyLongAndObnoxiousNameForIllustrativePurposes =
'a very long string for illustrative purposes'.length;
someReallyLongThingStoredInAMapWithAReallyBigName[pageletID] =
_someVariableThatWeAreCheckingForFalsiness
? Date.now() - _someVariableThatWeAreCheckingForFalsiness
: 0;
class x {
private readonly rawConfigFromFile$: BehaviorSubject<any> = new BehaviorSubject(notRead);
}
// Prettier stable
aParticularlyLongAndObnoxiousNameForIllustrativePurposes = anotherVeryLongNameForIllustrativePurposes;
+1 -8
View File
@@ -1,13 +1,7 @@
#### Improve formatting for React Hooks call (#10238 by @sosukesuzuki)
#### [IMPROVEMENT:1] Improve formatting for React Hooks calls (#10238 by @sosukesuzuki)
<!-- prettier-ignore -->
```js
// Input
const { firstName, lastName } = useMemo(
() => parseFullName(fullName),
[fullName]
);
// Prettier stable
const { firstName, lastName } = useMemo(() => parseFullName(fullName), [
fullName,
@@ -18,5 +12,4 @@ const { firstName, lastName } = useMemo(
() => parseFullName(fullName),
[fullName]
);
```
-15
View File
@@ -1,15 +0,0 @@
#### Disable Babel's error recovery for invalid escaped characters in identifiers (#10322 by @thorn0)
<!-- prettier-ignore -->
```js
// Input
const \u{20} = 1
// Prettier stable
const = 1;
// Prettier main
[error] stdin: SyntaxError: Invalid Unicode escape (1:7)
[error] > 1 | const \u{20} = 1
[error] | ^ ^
```
+8 -9
View File
@@ -1,22 +1,21 @@
#### Prevent wrapping object properties with short keys (#10335 by @thorn0)
#### [HIGHLIGHT] Prevent wrapping object properties with short keys (#10335 by @thorn0)
Line breaks after short property names in object literals often look unnatural. Even when such a line break yields a line length benefit of 1 or 2 characters, it rarely looks justified. Prettier main avoids line breaks after property names shorter than `tabWidth + 3`. The "+3" part has been chosen rather arbitrarily. It may be revised upward in future versions, provided there is a really good rationale.
Line breaks after short property names in object literals often look unnatural. Even when such a line break yields a line length benefit of 1 or 2 characters, it rarely looks justified. Prettier main avoids line breaks after property names shorter than `tabWidth + 3` – for example, 5 characters in the default configuration, or 7 characters with `tabWidth: 4`. This heuristic may be revised in future versions.
<!-- prettier-ignore -->
```js
// Input
const importantLink = {
url: "https://prettier.io/docs/en/rationale.html#what-prettier-is-concerned-about"
};
// Prettier stable
const importantLink = {
url:
"https://prettier.io/docs/en/rationale.html#what-prettier-is-concerned-about"
"https://prettier.io/docs/en/rationale.html#what-prettier-is-concerned-about",
gitHubUrl:
"https://github.com/prettier/prettier/blob/main/docs/rationale.md#what-prettier-is-concerned-about",
};
// Prettier main
const importantLink = {
url: "https://prettier.io/docs/en/rationale.html#what-prettier-is-concerned-about"
url: "https://prettier.io/docs/en/rationale.html#what-prettier-is-concerned-about",
gitHubUrl:
"https://github.com/prettier/prettier/blob/main/docs/rationale.md#what-prettier-is-concerned-about",
};
```
+1 -1
View File
@@ -1,4 +1,4 @@
#### Improve formatting for nested await expressions in heads of member and call expressions (#10342 by @thorn0)
#### [IMPROVEMENT:4] Improve formatting for nested await expressions in heads of member and call expressions (#10342 by @thorn0)
Even though Prettier tries to be helpful here, please don't write code like this. Have mercy upon your teammates and use intermediate variables.
+2 -3
View File
@@ -7,12 +7,11 @@ Support formatting for [Module Blocks Stage 2 proposal](https://github.com/tc39/
// Input
module { export let foo = "foo"; };
// Prettier main
// Prettier stable
SyntaxError: Unexpected token, expected ";"
// Prettier stable
// Prettier main
module {
export let foo = "foo";
};
```
+1 -1
View File
@@ -1,4 +1,4 @@
#### Fix missing parentheses for `yield` in `pipeline` (#10446 by @fisker)
#### Fix missing parentheses for `yield` in a pipeline (#10446 by @fisker)
<!-- prettier-ignore -->
```jsx
+31
View File
@@ -0,0 +1,31 @@
#### Make Babel’s error recovery more selective (#10495 by @fisker, #9787 by @sosukesuzuki, #10065, #10322 by @thorn0)
Previously, because of the [error recovery](https://babeljs.io/blog/2019/11/05/7.7.0#parser-error-recovery-10363httpsgithubcombabelbabelpull10363), the Babel parser was too permissive, which lead to all kinds of AST shapes that Prettier couldn’t print. Prettier main lets Babel recover only from a few harmless types of errors – for example, multiple `const` declarations with the same name. Anything else is reported as syntax errors.
<!-- prettier-ignore -->
```js
// Input
foo("a", , "b");
// Prettier stable
TypeError: Cannot read property 'type' of null
// Prettier main
[error] stdin: SyntaxError: Argument expression expected. (1:10)
[error] > 1 | foo("a", , "b");
[error] | ^
```
<!-- prettier-ignore -->
```js
// Input
const \u{20} = 1
// Prettier stable
const = 1;
// Prettier main
[error] stdin: SyntaxError: Invalid Unicode escape (1:7)
[error] > 1 | const \u{20} = 1
[error] | ^ ^
```
+1 -8
View File
@@ -1,16 +1,9 @@
#### Improve detection of AMD `define` (#10528 by @thorn0)
Prettier special-cases AMD `define` calls to avoid unexpected line breaks in them. `define` calls that weren't expression statements and had nothing to do with AMD were formatted this way too. This happens no longer.
Prettier special-cases AMD `define` calls to avoid unexpected line breaks in them. We now only format `define` calls if they are at the top level of a function or program and are passed arguments in the way AMD expects.
<!-- prettier-ignore -->
```jsx
// Input
const someVariable = define(
"some string literal",
anotherVariable,
yetAnotherVariable
);
// Prettier stable
const someVariable = define("some string literal", anotherVariable, yetAnotherVariable);
+21
View File
@@ -0,0 +1,21 @@
#### Fix duplicated `prettier-ignore` comments (#10666 by @fisker)
<!-- prettier-ignore -->
```jsx
// Input
foo = a.
// prettier-ignore
b;
// Prettier stable
foo =
// prettier-ignore
a.
// prettier-ignore
b;
// Prettier main
foo = a.
// prettier-ignore
b;
```
+20
View File
@@ -0,0 +1,20 @@
#### [IMPROVEMENT:5] Improve formatting for `do` expressions in function calls (#10693 by @sosukesuzuki)
“`do` expressions” are a [stage 1 ECMAScript proposal](https://github.com/tc39/proposal-do-expressions).
<!-- prettier-ignore -->
```js
// Prettier stable
expect(
do {
var bar = "foo";
bar;
}
).toBe("foo");
// Prettier main
expect(do {
var bar = "foo";
bar;
}).toBe("foo");
```
+40
View File
@@ -0,0 +1,40 @@
#### Process conditional groups in `mapDoc` (#10695 by @thorn0)
In particular, this fixes broken substitutions in HTML-in-JS.
<!-- prettier-ignore -->
```jsx
// Input
export default function include_photoswipe(
gallery_selector = ".my-gallery"
): string {
return /* HTML */ `
<script>
window.addEventListener("load", () =>
initPhotoSwipeFromDOM("${gallery_selector}")
);
</script>`;
}
// Prettier stable
export default function include_photoswipe(
gallery_selector = ".my-gallery"
): string {
return /* HTML */ ` <script>
window.addEventListener("load", () =>
initPhotoSwipeFromDOM("PRETTIER_HTML_PLACEHOLDER_0_13_IN_JS")
);
</script>`;
}
// Prettier main
export default function include_photoswipe(
gallery_selector = ".my-gallery"
): string {
return /* HTML */ ` <script>
window.addEventListener("load", () =>
initPhotoSwipeFromDOM("${gallery_selector}")
);
</script>`;
}
```
+24
View File
@@ -0,0 +1,24 @@
#### Avoid arg expansion that leads to broken code (#10712 by @thorn0)
<!-- prettier-ignore -->
```jsx
// Input
glimseGlyphsHazardNoopsTieTie(({ a, b = () => {
console.log();
}}) => averredBathersBoxroomBuggyNurl());
// Prettier stable
glimseGlyphsHazardNoopsTieTie(({ a, b = () => {
console.log();
} }) => averredBathersBoxroomBuggyNurl());
// Prettier main
glimseGlyphsHazardNoopsTieTie(
({
a,
b = () => {
console.log();
},
}) => averredBathersBoxroomBuggyNurl()
);
```
+2 -4
View File
@@ -1,9 +1,7 @@
#### Improve comment format (#9672 by @fisker)
When multiple comments on same line, the result became unstable.
#### Fix unstable multiple comments on the same line (#9672 by @fisker)
<!-- prettier-ignore -->
```jsx
```js
// Input
a;
/*1*//*2*/
+3 -5
View File
@@ -1,10 +1,10 @@
#### Fix node ends at `rangeStart` was formatted (#9704 by @fisker)
#### Don’t format nodes that end just before `rangeStart` (#9704 by @fisker)
Previously a node ends at `rangeStart` in considered in range, now it's excluded.
Previously, when [range formatting](https://prettier.io/docs/en/options.html#range) was performed, such nodes were considered part of the range, now they're excluded. This affects other languages that the range formatting feature supports, not only JavaScript.
<!-- prettier-ignore -->
```js
// Prettier stable
// Input
foo = 1.0000;bar = 1.0000;baz=1.0000;
^^^^^^^^^^^^^ Range
@@ -15,5 +15,3 @@ bar = 1.0;baz=1.0000;
// Prettier main
foo = 1.0000;bar = 1.0;baz=1.0000;
```
_This effect other languages support range format too._
+2
View File
@@ -1,5 +1,7 @@
#### Fix inconsistent language comment detection (#9743 by @fisker)
An `/* HTML */` comment should directly precede a template literal for the latter to be recognized as HTML-in-JS. Previously, the comment was erroneously recognized is some other locations.
<!-- prettier-ignore -->
```jsx
// Input
-15
View File
@@ -1,15 +0,0 @@
#### Disable Babel's error recovery for omitted call arguments (#9787 by @sosukesuzuki, #10065 by @thorn0)
<!-- prettier-ignore -->
```js
// Input
foo("a", , "b");
// Prettier stable
TypeError: Cannot read property 'type' of null
// Prettier main
[error] stdin: SyntaxError: Argument expression expected. (1:10)
[error] > 1 | foo("a", , "b");
[error] | ^
```
+1 -11
View File
@@ -1,17 +1,7 @@
#### Refine formatting of curried arrow functions (#9992, #10543 by @sosukesuzuki & @thorn0)
#### [IMPROVEMENT:0] Refine formatting of curried arrow functions (#9992, #10543 by @sosukesuzuki & @thorn0)
<!-- prettier-ignore -->
```js
// Input
const currying =
(argument1) =>
(argument2) =>
(argument3) =>
(argument4) =>
(argument5) =>
(argument6) =>
foo;
// Prettier stable
const currying = (argument1) => (argument2) => (argument3) => (argument4) => (
argument5
-5
View File
@@ -2,11 +2,6 @@
<!-- prettier-ignore -->
```ts
// Input
users.map((user: User): User => {
return user;
})
// Prettier stable
users.map(
(user: User): User => {
+12 -21
View File
@@ -3,29 +3,20 @@
<!-- prettier-ignore -->
```ts
// Input
const getAccountCount = async () =>
(await
((await (
await focusOnSection(BOOKMARKED_PROJECTS_SECTION_NAME)
).findItem("My bookmarks")) as TreeItem
).getChildren()
).length
const accountCount = (findItemInSection(BOOKMARKED_PROJECTS_SECTION_NAME,
"My bookmarks") as TreeItem).getChildren().length;
// Prettier stable
const getAccountCount = async () =>
(
await ((await (
await focusOnSection(BOOKMARKED_PROJECTS_SECTION_NAME)
).findItem("My bookmarks")) as TreeItem).getChildren()
).length;
const accountCount = (findItemInSection(
BOOKMARKED_PROJECTS_SECTION_NAME,
"My bookmarks"
) as TreeItem).getChildren().length;
// Prettier main
const getAccountCount = async () =>
(
await (
(await (
await focusOnSection(BOOKMARKED_PROJECTS_SECTION_NAME)
).findItem("My bookmarks")) as TreeItem
).getChildren()
).length;
const accountCount = (
findItemInSection(
BOOKMARKED_PROJECTS_SECTION_NAME,
"My bookmarks"
) as TreeItem
).getChildren().length;
```
+4 -4
View File
@@ -7,10 +7,10 @@
// Input
type T = abstract new () => void;
// Prettier main
// Prettier stable
SyntaxError: Unexpected token, expected ";" (1:19)
// Prettier stable
// Prettier main
type T = abstract new () => void;
```
@@ -22,10 +22,10 @@ type T = abstract new () => void;
// Input
import type A = require("A");
// Prettier main
// Prettier stable
SyntaxError: Only ECMAScript imports may use 'import type'.
// Prettier stable
// Prettier main
import type A = require("A");
```
+13
View File
@@ -0,0 +1,13 @@
#### Don’t print parens around nested type assertions (#10702 by @thorn0)
<!-- prettier-ignore -->
```ts
// Input
foo as unknown as Bar
// Prettier stable
(foo as unknown) as Bar;
// Prettier main
foo as unknown as Bar;
```
+1 -1
View File
@@ -1,6 +1,6 @@
#### Fix missing comments in `MethodDefinition` (#9872 by @fisker)
`typescript` parser only, `babel-ts` don't have this issue.
`typescript` parser only, `babel-ts` doesn't have this issue.
<!-- prettier-ignore -->
```typescript
+1
View File
@@ -199,6 +199,7 @@
"kddeisz",
"kddnewton",
"Kearney",
"keyframes",
"keyof",
"Khatri",
"l’objectif",
+30 -8
View File
@@ -20,7 +20,7 @@ yarn add --dev --exact prettier
<!--END_DOCUSAURUS_CODE_TABS-->
Then, create an empty config file to let editors and other tooling know you are using Prettier:
Then, create an empty config file to let editors and other tools know you are using Prettier:
<!-- Note: `echo "{}" > .prettierrc.json` would result in `"{}"<SPACE>` on Windows. The below version works in cmd.exe, bash, zsh, fish. -->
@@ -68,7 +68,7 @@ yarn prettier --write .
If you have a CI setup, run the following as part of it to make sure that everyone runs Prettier. This avoids merge conflicts and other collaboration issues!
```bash
prettier --check .
npx prettier --check .
```
`--check` is like `--write`, but only checks that files are already formatted, rather than overwriting them. `prettier --write` and `prettier --check` are the most common ways to run Prettier.
@@ -93,15 +93,37 @@ If you use ESLint, install [eslint-config-prettier](https://github.com/prettier/
In addition to running Prettier from the command line (`prettier --write`), checking formatting in CI, and running Prettier from your editor, many people like to run Prettier as a pre-commit hook as well. This makes sure all your commits are formatted, without having to wait for your CI build to finish.
For example, you can add the following to your `package.json` to have ESLint and Prettier run before each commit, via [lint-staged](https://github.com/okonet/lint-staged) and [husky](https://github.com/typicode/husky).
For example, you can do the following to have Prettier run before each commit:
1. Install [husky](https://github.com/typicode/husky) and [lint-staged](https://github.com/okonet/lint-staged):
<!--DOCUSAURUS_CODE_TABS-->
<!--npm-->
```bash
npm install --save-dev husky lint-staged
npx husky install
npm set-script prepare "husky install"
npx husky add .husky/pre-commit "npx lint-staged"
```
<!--yarn-->
```bash
yarn add --dev husky lint-staged
npx husky install
npm set-script prepare "husky install"
npx husky add .husky/pre-commit "npx lint-staged"
```
> If you use Yarn 2, see https://typicode.github.io/husky/#/?id=yarn-2
<!--END_DOCUSAURUS_CODE_TABS-->
2. Add the following to your `package.json`:
```json
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"**/*": "prettier --write --ignore-unknown"
}
+13 -29
View File
@@ -15,7 +15,7 @@ _Make sure Prettier is installed and is in your [`devDependencies`](https://docs
npx mrm lint-staged
```
This will install [simple-git-hooks](https://github.com/toplenboren/simple-git-hooks) and [lint-staged](https://github.com/okonet/lint-staged), then add a configuration to the project’s `package.json` that will automatically format supported files in a pre-commit hook.
This will install [husky](https://github.com/typicode/husky) and [lint-staged](https://github.com/okonet/lint-staged), then add a configuration to the project’s `package.json` that will automatically format supported files in a pre-commit hook.
Read more at the [lint-staged](https://github.com/okonet/lint-staged#configuration) repo.
@@ -29,29 +29,21 @@ Install it along with [husky](https://github.com/typicode/husky):
<!--npm-->
```bash
npm install --save-dev pretty-quick husky
npx husky-init
npm install --save-dev pretty-quick
npx husky set .husky/pre-commit "pretty-quick --staged"
```
<!--yarn-->
```bash
yarn add --dev pretty-quick husky
npx husky-init # add --yarn2 for Yarn 2
yarn add --dev pretty-quick
yarn husky set .husky/pre-commit "pretty-quick --staged"
```
<!--END_DOCUSAURUS_CODE_TABS-->
Add this to your `package.json`:
```json
{
"husky": {
"hooks": {
"pre-commit": "pretty-quick --staged"
}
}
}
```
Read more at the [pretty-quick](https://github.com/azz/pretty-quick) repo.
## Option 3. [pre-commit](https://github.com/pre-commit/pre-commit)
@@ -86,29 +78,21 @@ Git-format-staged requires Python v3 or v2.7. Python is usually pre-installed on
<!--npm-->
```bash
npm install --save-dev git-format-staged husky
npx husky-init
npm install --save-dev git-format-staged
npx husky set .husky/pre-commit "git-format-staged -f 'prettier --ignore-unknown --stdin --stdin-filepath \"{}\"' ."
```
<!--yarn-->
```bash
yarn add --dev git-format-staged husky
npx husky-init # add --yarn2 for Yarn 2
yarn add --dev git-format-staged
yarn husky set .husky/pre-commit "git-format-staged -f 'prettier --ignore-unknown --stdin --stdin-filepath \"{}\"' ."
```
<!--END_DOCUSAURUS_CODE_TABS-->
and add this config to your `package.json`:
```json
{
"husky": {
"hooks": {
"pre-commit": "git-format-staged -f 'prettier --ignore-unknown --stdin --stdin-filepath \"{}\"' ."
}
}
}
```
Add or remove file extensions to suit your project. Note that regardless of which extensions you list formatting will respect any `.prettierignore` files in your project.
To read about how git-format-staged works see [Automatic Code Formatting for Partially-Staged Files](https://www.olioapps.com/blog/automatic-code-formatting/).
+5 -5
View File
@@ -20,7 +20,7 @@ process.env.PRETTIER_DIR = PRETTIER_DIR;
const testPathIgnorePatterns = [];
let transform = {};
if (TEST_STANDALONE) {
testPathIgnorePatterns.push("<rootDir>/tests_integration/");
testPathIgnorePatterns.push("<rootDir>/tests/integration/");
}
if (isProduction) {
// `esm` bundles need transform
@@ -48,12 +48,12 @@ if (isProduction) {
} else {
// Only test bundles for production
testPathIgnorePatterns.push(
"<rootDir>/tests_integration/__tests__/bundle.js"
"<rootDir>/tests/integration/__tests__/bundle.js"
);
}
module.exports = {
setupFiles: ["<rootDir>/tests_config/setup.js"],
setupFiles: ["<rootDir>/tests/config/setup.js"],
snapshotSerializers: [
"jest-snapshot-serializer-raw",
"jest-snapshot-serializer-ansi",
@@ -68,8 +68,8 @@ module.exports = {
],
coverageReporters: ["text", "lcov"],
moduleNameMapper: {
"prettier-local": "<rootDir>/tests_config/require_prettier.js",
"prettier-standalone": "<rootDir>/tests_config/require_standalone.js",
"prettier-local": "<rootDir>/tests/config/require-prettier.js",
"prettier-standalone": "<rootDir>/tests/config/require-standalone.js",
},
modulePathIgnorePatterns: ["<rootDir>/dist", "<rootDir>/website/static/lib"],
testEnvironment: "node",
+1 -1
View File
@@ -1,3 +1,3 @@
[build.environment]
NODE_VERSION = "12"
NODE_VERSION = "14"
YARN_VERSION = "1.22.10"
+24 -24
View File
@@ -20,16 +20,16 @@
"bin"
],
"dependencies": {
"@angular/compiler": "11.2.7",
"@angular/compiler": "11.2.12",
"@babel/code-frame": "7.12.13",
"@babel/parser": "7.13.11",
"@glimmer/syntax": "0.77.6",
"@babel/parser": "7.13.16",
"@glimmer/syntax": "0.79.0",
"@iarna/toml": "2.2.5",
"@typescript-eslint/typescript-estree": "4.19.0",
"@typescript-eslint/typescript-estree": "4.22.0",
"angular-estree-parser": "2.3.0",
"angular-html-parser": "1.7.1",
"angular-html-parser": "1.8.0",
"camelcase": "6.2.0",
"chalk": "4.1.0",
"chalk": "4.1.1",
"ci-info": "3.1.1",
"cjk-regex": "2.0.1",
"cosmiconfig": "7.0.0",
@@ -43,7 +43,7 @@
"fast-glob": "3.2.5",
"fast-json-stable-stringify": "2.1.0",
"find-parent-dir": "0.3.0",
"flow-parser": "0.147.0",
"flow-parser": "0.149.0",
"get-stdin": "8.0.0",
"globby": "11.0.3",
"graphql": "15.5.0",
@@ -56,9 +56,9 @@
"json5": "2.2.0",
"leven": "3.1.0",
"lines-and-columns": "1.1.6",
"linguist-languages": "7.13.0",
"linguist-languages": "7.14.0",
"lodash": "4.17.21",
"mem": "8.1.0",
"mem": "8.1.1",
"meriyah": "4.1.5",
"minimatch": "3.0.4",
"minimist": "1.2.5",
@@ -80,7 +80,7 @@
"semver": "7.3.5",
"string-width": "4.2.2",
"strip-ansi": "6.0.0",
"typescript": "4.2.3",
"typescript": "4.2.4",
"unicode-regex": "3.0.0",
"unified": "9.2.1",
"vnopts": "1.0.2",
@@ -88,10 +88,10 @@
"yaml-unist-parser": "1.3.1"
},
"devDependencies": {
"@babel/core": "7.13.13",
"@babel/preset-env": "7.13.12",
"@babel/types": "7.13.13",
"@glimmer/reference": "0.77.6",
"@babel/core": "7.14.0",
"@babel/preset-env": "7.13.15",
"@babel/types": "7.13.17",
"@glimmer/reference": "0.78.2",
"@rollup/plugin-alias": "3.1.2",
"@rollup/plugin-babel": "5.3.0",
"@rollup/plugin-commonjs": "18.0.0",
@@ -99,32 +99,32 @@
"@rollup/plugin-node-resolve": "11.2.1",
"@rollup/plugin-replace": "2.4.2",
"@types/estree": "0.0.47",
"@types/node": "14.14.0",
"@types/node": "15.0.1",
"babel-jest": "26.6.3",
"babel-loader": "8.2.2",
"benchmark": "2.1.4",
"builtin-modules": "3.2.0",
"cross-env": "7.0.3",
"cspell": "4.2.8",
"eslint": "7.23.0",
"eslint-config-prettier": "8.1.0",
"eslint": "7.25.0",
"eslint-config-prettier": "8.3.0",
"eslint-formatter-friendly": "7.0.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-jest": "24.3.2",
"eslint-plugin-jest": "24.3.6",
"eslint-plugin-prettier-internal-rules": "link:scripts/tools/eslint-plugin-prettier-internal-rules",
"eslint-plugin-react": "7.23.1",
"eslint-plugin-unicorn": "29.0.0",
"eslint-plugin-react": "7.23.2",
"eslint-plugin-unicorn": "31.0.0",
"execa": "5.0.0",
"jest": "26.6.3",
"jest-snapshot-serializer-ansi": "1.0.0",
"jest-snapshot-serializer-raw": "1.1.0",
"jest-watch-typeahead": "0.6.1",
"jest-snapshot-serializer-raw": "1.2.0",
"jest-watch-typeahead": "0.6.3",
"npm-run-all": "4.1.5",
"path-browserify": "1.0.1",
"prettier": "2.2.1",
"pretty-bytes": "5.6.0",
"rimraf": "3.0.2",
"rollup": "2.43.1",
"rollup": "2.46.0",
"rollup-plugin-polyfill-node": "0.6.2",
"rollup-plugin-terser": "7.0.2",
"shelljs": "0.8.4",
@@ -139,7 +139,7 @@
"test:dev-package": "cross-env INSTALL_PACKAGE=1 jest",
"test:dist": "cross-env NODE_ENV=production jest",
"test:dist-standalone": "cross-env NODE_ENV=production TEST_STANDALONE=1 jest",
"test:integration": "jest tests_integration",
"test:integration": "jest tests/integration",
"perf:repeat": "yarn && yarn build && cross-env NODE_ENV=production node ./dist/bin-prettier.js --debug-repeat ${PERF_REPEAT:-1000} --loglevel debug ${PERF_FILE:-./index.js} > /dev/null",
"perf:repeat-inspect": "yarn && yarn build && cross-env NODE_ENV=production node --inspect-brk ./dist/bin-prettier.js --debug-repeat ${PERF_REPEAT:-1000} --loglevel debug ${PERF_FILE:-./index.js} > /dev/null",
"perf:benchmark": "yarn && yarn build && cross-env NODE_ENV=production node ./dist/bin-prettier.js --debug-benchmark --loglevel debug ${PERF_FILE:-./index.js} > /dev/null",
+6 -6
View File
@@ -1,12 +1,12 @@
{
"private": true,
"dependencies": {
"chalk": "2.4.1",
"execa": "0.10.0",
"minimist": "1.2.3",
"chalk": "4.1.1",
"execa": "5.0.0",
"minimist": "1.2.5",
"node-fetch": "2.6.1",
"outdent": "0.7.1",
"semver": "5.5.0",
"string-width": "2.1.1"
"outdent": "0.8.0",
"semver": "7.3.5",
"string-width": "4.2.0"
}
}
+8 -7
View File
@@ -2,15 +2,14 @@
"use strict";
const { exec, execSync } = require("child_process");
const { exec } = require("child_process");
async function run() {
const chalk = require("chalk");
const { string: outdentString } = require("outdent");
const minimist = require("minimist");
const semver = require("semver");
const { readJson } = require("./utils");
const { string: outdentString } = require("outdent");
const { runGit, readJson } = require("./utils");
const params = minimist(process.argv.slice(2), {
string: ["version"],
@@ -18,9 +17,11 @@ async function run() {
alias: { v: "version" },
});
const previousVersion = execSync("git describe --tags --abbrev=0")
.toString()
.trim();
const { stdout: previousVersion } = await runGit([
"describe",
"--tags",
"--abbrev=0",
]);
if (semver.parse(previousVersion) === null) {
throw new Error(`Unexpected previousVersion: ${previousVersion}`);
+14 -13
View File
@@ -1,32 +1,33 @@
"use strict";
const fs = require("fs");
const execa = require("execa");
const semver = require("semver");
const { logPromise, readJson, writeJson } = require("../utils");
const {
runYarn,
runGit,
logPromise,
readJson,
writeJson,
} = require("../utils");
async function format() {
await execa("yarn", ["lint:eslint", "--fix"]);
await execa("yarn", ["lint:prettier", "--write"]);
await runYarn(["lint:eslint", "--fix"]);
await runYarn(["lint:prettier", "--write"]);
}
async function commit(version) {
await execa("git", [
"commit",
"-am",
`Bump Prettier dependency to ${version}`,
]);
await runGit(["commit", "-am", `Bump Prettier dependency to ${version}`]);
// Add rev to `.git-blame-ignore-revs` file
const file = ".git-blame-ignore-revs";
const mark = "# Prettier bump after release";
const rev = await execa.stdout("git", ["rev-parse", "HEAD"]);
const { stdout: rev } = await runGit("git", ["rev-parse", "HEAD"]);
let text = fs.readFileSync(file, "utf8");
text = text.replace(mark, `${mark}\n# ${version}\n${rev}`);
fs.writeFileSync(file, text);
await execa("git", ["commit", "-am", `Git blame ignore ${version}`]);
await runGit(["commit", "-am", `Git blame ignore ${version}`]);
await execa("git", ["push"]);
await runGit(["push"]);
}
async function bump({
@@ -52,7 +53,7 @@ module.exports = async function (params) {
await logPromise(
"Installing Prettier",
execa("yarn", ["add", "--dev", `prettier@${version}`])
runYarn(["add", "--dev", `prettier@${version}`])
);
await logPromise("Updating files", format());
+2 -2
View File
@@ -1,9 +1,9 @@
"use strict";
const execa = require("execa");
const { runGit } = require("../utils");
module.exports = async function () {
const status = await execa.stdout("git", ["status", "--porcelain"]);
const { stdout: status } = await runGit(["status", "--porcelain"]);
if (status) {
throw new Error(
@@ -1,13 +1,13 @@
"use strict";
const execa = require("execa");
const { logPromise } = require("../utils");
const { runYarn, runGit, logPromise } = require("../utils");
async function install() {
await execa("rm", ["-rf", "node_modules"]);
await execa("yarn", ["install"]);
await runYarn(["install"]);
const status = await execa.stdout("git", ["ls-files", "-m"]);
const { stdout: status } = await runGit(["ls-files", "-m"]);
if (status) {
throw new Error(
"The lockfile needs to be updated, commit it before making the release."
+6 -7
View File
@@ -2,9 +2,8 @@
const chalk = require("chalk");
const { string: outdentString } = require("outdent");
const fetch = require("node-fetch");
const execa = require("execa");
const { logPromise } = require("../utils");
const { fetchText, logPromise } = require("../utils");
const SCHEMA_REPO = "SchemaStore/schemastore";
const SCHEMA_PATH = "src/schemas/json/prettierrc.json";
@@ -14,15 +13,15 @@ const EDIT_URL = `https://github.com/${SCHEMA_REPO}/edit/master/${SCHEMA_PATH}`;
// Any optional or manual step can be warned in this script.
async function checkSchema() {
const schema = await execa.stdout("node", ["scripts/generate-schema.js"]);
const { stdout: schema } = await execa("node", [
"scripts/generate-schema.js",
]);
const remoteSchema = await logPromise(
"Checking current schema in SchemaStore",
fetch(RAW_URL)
.then((r) => r.text())
.then((t) => t.trim())
fetchText(RAW_URL)
);
if (schema === remoteSchema) {
if (schema === remoteSchema.trim()) {
return;
}
+5 -6
View File
@@ -1,13 +1,12 @@
"use strict";
const execa = require("execa");
const { logPromise } = require("../utils");
const { runGit, logPromise } = require("../utils");
async function pushGit({ version }) {
await execa("git", ["commit", "-am", `Release ${version}`]);
await execa("git", ["tag", "-a", version, "-m", `Release ${version}`]);
await execa("git", ["push"]);
await execa("git", ["push", "--tags"]);
await runGit(["commit", "-am", `Release ${version}`]);
await runGit(["tag", "-a", version, "-m", `Release ${version}`]);
await runGit(["push"]);
await runGit(["push", "--tags"]);
}
module.exports = function (params) {
@@ -1,16 +1,12 @@
"use strict";
const chalk = require("chalk");
const fetch = require("node-fetch");
const execa = require("execa");
const { logPromise, processFile } = require("../utils");
const { runGit, fetchText, logPromise, processFile } = require("../utils");
async function update() {
const npmPage = await logPromise(
"Fetching npm dependents count",
fetch("https://www.npmjs.com/package/prettier").then((response) =>
response.text()
)
fetchText("https://www.npmjs.com/package/prettier")
);
const dependentsCountNpm = Number(
npmPage.match(/"dependentsCount":(\d+),/)[1]
@@ -23,9 +19,7 @@ async function update() {
const githubPage = await logPromise(
"Fetching github dependents count",
fetch(
"https://github.com/prettier/prettier/network/dependents"
).then((response) => response.text())
fetchText("https://github.com/prettier/prettier/network/dependents")
);
const dependentsCountGithub = Number(
githubPage
@@ -55,20 +49,17 @@ async function update() {
const isUpdated = await logPromise(
"Checking if dependents count has been updated",
execa("git", ["diff", "--name-only"]).then(
({ stdout }) => stdout === "website/pages/en/index.js"
)
async () =>
(await runGit(["diff", "--name-only"])).stdout ===
"website/pages/en/index.js"
);
if (isUpdated) {
logPromise(
"Committing and pushing to remote",
(async () => {
await execa("git", ["add", "."]);
await execa("git", ["commit", "-m", "Update dependents count"]);
await execa("git", ["push"]);
})()
);
logPromise("Committing and pushing to remote", async () => {
await runGit(["add", "."]);
await runGit(["commit", "-m", "Update dependents count"]);
await runGit(["push"]);
});
}
}
+8 -3
View File
@@ -1,7 +1,12 @@
"use strict";
const execa = require("execa");
const { logPromise, readJson, writeJson, processFile } = require("../utils");
const {
runYarn,
logPromise,
readJson,
writeJson,
processFile,
} = require("../utils");
async function bump({ version }) {
const pkg = await readJson("package.json");
@@ -24,7 +29,7 @@ async function bump({ version }) {
content.replace(/(\/\/unpkg\.com\/prettier@).*?\//g, `$1${version}/`)
);
await execa("yarn", ["update-stable-docs"], {
await runYarn(["update-stable-docs"], {
cwd: "./website",
});
}
+35 -17
View File
@@ -6,6 +6,7 @@ const fs = require("fs");
const chalk = require("chalk");
const execa = require("execa");
const stringWidth = require("string-width");
const fetch = require("node-fetch");
const OK = chalk.bgGreen.black(" DONE ");
const FAIL = chalk.bgRed.black(" FAIL ");
@@ -19,27 +20,37 @@ function fitTerminal(input) {
return input;
}
function logPromise(name, promise) {
async function logPromise(name, promiseOrAsyncFunction) {
const promise =
typeof promiseOrAsyncFunction === "function"
? promiseOrAsyncFunction()
: promiseOrAsyncFunction;
process.stdout.write(fitTerminal(name));
return promise
.then((result) => {
process.stdout.write(`${OK}\n`);
return result;
})
.catch((err) => {
process.stdout.write(`${FAIL}\n`);
throw err;
});
try {
const result = await promise;
process.stdout.write(`${OK}\n`);
return result;
} catch (error) {
process.stdout.write(`${FAIL}\n`);
throw error;
}
}
function runYarn(script) {
return execa("yarn", [
"--silent",
...(Array.isArray(script) ? script : [script]),
]).catch((error) => {
throw new Error(`\`yarn ${script}\` failed\n${error.stdout}`);
});
async function runYarn(args, options) {
args = Array.isArray(args) ? args : [args];
try {
return await execa("yarn", ["--silent", ...args], options);
} catch (error) {
throw new Error(`\`yarn ${args.join(" ")}\` failed\n${error.stdout}`);
}
}
function runGit(args, options) {
args = Array.isArray(args) ? args : [args];
return execa("git", args, options);
}
function waitForEnter() {
@@ -75,8 +86,15 @@ function processFile(filename, fn) {
fs.writeFileSync(filename, fn(content));
}
async function fetchText(url) {
const response = await fetch(url);
return response.text();
}
module.exports = {
runYarn,
runGit,
fetchText,
logPromise,
processFile,
readJson,
+186 -134
View File
@@ -2,155 +2,207 @@
# yarn lockfile v1
ansi-regex@^3.0.0:
ansi-regex@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
ansi-styles@^4.1.0:
version "4.3.0"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==
dependencies:
color-convert "^2.0.1"
chalk@4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
dependencies:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
color-convert@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
dependencies:
color-name "~1.1.4"
color-name@~1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
dependencies:
path-key "^3.1.0"
shebang-command "^2.0.0"
which "^2.0.1"
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
execa@5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-5.0.0.tgz#4029b0007998a841fbd1032e5f4de86a3c1e3376"
integrity sha512-ov6w/2LCiuyO4RLYGdpFGjkcs0wMTgGE8PrkTHikeUy5iJekXyPIKUjifk5CsE0pt7sMCrMZ3YNqoCj6idQOnQ==
dependencies:
cross-spawn "^7.0.3"
get-stream "^6.0.0"
human-signals "^2.1.0"
is-stream "^2.0.0"
merge-stream "^2.0.0"
npm-run-path "^4.0.1"
onetime "^5.1.2"
signal-exit "^3.0.3"
strip-final-newline "^2.0.0"
get-stream@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-6.0.0.tgz#3e0012cb6827319da2706e601a1583e8629a6718"
integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==
has-flag@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
human-signals@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/human-signals/-/human-signals-2.1.0.tgz#dc91fcba42e4d06e4abaed33b3e7a3c02f514ea0"
integrity sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
ansi-styles@^3.2.1:
version "3.2.1"
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
dependencies:
color-convert "^1.9.0"
chalk@2.4.1:
version "2.4.1"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.1.tgz#18c49ab16a037b6eb0152cc83e3471338215b66e"
dependencies:
ansi-styles "^3.2.1"
escape-string-regexp "^1.0.5"
supports-color "^5.3.0"
color-convert@^1.9.0:
version "1.9.1"
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.1.tgz#c1261107aeb2f294ebffec9ed9ecad529a6097ed"
dependencies:
color-name "^1.1.1"
color-name@^1.1.1:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
cross-spawn@^6.0.0:
version "6.0.5"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4"
dependencies:
nice-try "^1.0.4"
path-key "^2.0.1"
semver "^5.5.0"
shebang-command "^1.2.0"
which "^1.2.9"
escape-string-regexp@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
execa@0.10.0:
version "0.10.0"
resolved "https://registry.yarnpkg.com/execa/-/execa-0.10.0.tgz#ff456a8f53f90f8eccc71a96d11bdfc7f082cb50"
dependencies:
cross-spawn "^6.0.0"
get-stream "^3.0.0"
is-stream "^1.1.0"
npm-run-path "^2.0.0"
p-finally "^1.0.0"
signal-exit "^3.0.0"
strip-eof "^1.0.0"
get-stream@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
has-flag@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
is-fullwidth-code-point@^2.0.0:
is-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
is-stream@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
isexe@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
minimist@1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.3.tgz#3db5c0765545ab8637be71f333a104a965a9ca3f"
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
nice-try@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/nice-try/-/nice-try-1.0.4.tgz#d93962f6c52f2c1558c0fbda6d512819f1efe1c4"
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
mimic-fn@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
minimist@1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
node-fetch@2.6.1:
version "2.6.1"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
npm-run-path@^2.0.0:
npm-run-path@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-4.0.1.tgz#b7ecd1e5ed53da8e37a55e1c2269e0b97ed748ea"
integrity sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==
dependencies:
path-key "^3.0.0"
onetime@^5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e"
integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==
dependencies:
mimic-fn "^2.1.0"
outdent@0.8.0:
version "0.8.0"
resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.8.0.tgz#2ebc3e77bf49912543f1008100ff8e7f44428eb0"
integrity sha512-KiOAIsdpUTcAXuykya5fnVVT+/5uS0Q1mrkRHcF89tpieSmY33O/tmc54CqwA+bfhbtEfZUNLHaPUiB9X3jt1A==
path-key@^3.0.0, path-key@^3.1.0:
version "3.1.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375"
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
semver@7.3.5:
version "7.3.5"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
dependencies:
lru-cache "^6.0.0"
shebang-command@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea"
integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==
dependencies:
shebang-regex "^3.0.0"
shebang-regex@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
signal-exit@^3.0.3:
version "3.0.3"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.3.tgz#a1410c2edd8f077b08b4e253c8eacfcaf057461c"
integrity sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==
string-width@4.2.0:
version "4.2.0"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.0.tgz#952182c46cc7b2c313d1596e623992bd163b72b5"
integrity sha512-zUz5JD+tgqtuDjMhwIg5uFVV3dtqZ9yQJlZVfq4I01/K5Paj5UHj7VyrQOJvzawSVlKpObApbfD0Ed6yJc+1eg==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.0"
strip-ansi@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532"
integrity sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==
dependencies:
ansi-regex "^5.0.0"
strip-final-newline@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
supports-color@^7.1.0:
version "7.2.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==
dependencies:
has-flag "^4.0.0"
which@^2.0.1:
version "2.0.2"
resolved "https://registry.yarnpkg.com/npm-run-path/-/npm-run-path-2.0.2.tgz#35a9232dfa35d7067b4cb2ddf2357b1871536c5f"
dependencies:
path-key "^2.0.0"
outdent@0.7.1:
version "0.7.1"
resolved "https://registry.yarnpkg.com/outdent/-/outdent-0.7.1.tgz#e9b400443622a97760b0bc74fa3223252ccd02a2"
integrity sha512-VjIzdUHunL74DdhcwMDt5FhNDQ8NYmTkuW0B+usIV2afS9aWT/1c9z1TsnFW349TP3nxmYeUl7Z++XpJRByvgg==
p-finally@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
path-key@^2.0.0, path-key@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40"
semver@5.5.0, semver@^5.5.0:
version "5.5.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.5.0.tgz#dc4bbc7a6ca9d916dee5d43516f0092b58f7b8ab"
shebang-command@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-1.2.0.tgz#44aac65b695b03398968c39f363fee5deafdf1ea"
dependencies:
shebang-regex "^1.0.0"
shebang-regex@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-1.0.0.tgz#da42f49740c0b42db2ca9728571cb190c98efea3"
signal-exit@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
string-width@2.1.1:
version "2.1.1"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
dependencies:
is-fullwidth-code-point "^2.0.0"
strip-ansi "^4.0.0"
strip-ansi@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
dependencies:
ansi-regex "^3.0.0"
strip-eof@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/strip-eof/-/strip-eof-1.0.0.tgz#bb43ff5598a6eb05d89b59fcd129c983313606bf"
supports-color@^5.3.0:
version "5.4.0"
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.4.0.tgz#1c6b337402c2137605efe19f10fec390f6faab54"
dependencies:
has-flag "^3.0.0"
which@^1.2.9:
version "1.3.1"
resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a"
resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1"
integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==
dependencies:
isexe "^2.0.0"
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==
@@ -7,6 +7,7 @@ module.exports = {
"directly-loc-start-end": require("./directly-loc-start-end"),
"flat-ast-path-call": require("./flat-ast-path-call"),
"jsx-identifier-case": require("./jsx-identifier-case"),
"no-conflicting-comment-check-flags": require("./no-conflicting-comment-check-flags"),
"no-doc-builder-concat": require("./no-doc-builder-concat"),
"no-empty-flat-contents-for-if-break": require("./no-empty-flat-contents-for-if-break"),
"no-identifier-n": require("./no-identifier-n"),
@@ -0,0 +1,87 @@
"use strict";
const MESSAGE_ID_UNIQUE = "unique";
const MESSAGE_ID_CONFLICTING = "conflicting";
const conflictingFlags = [
["Leading", "Trailing", "Dangling"],
["Block", "Line"],
];
const isCommentCheckFlags = (node) =>
node.type === "MemberExpression" &&
!node.computed &&
!node.optional &&
node.object.type === "Identifier" &&
node.object.name === "CommentCheckFlags" &&
node.property.type === "Identifier";
const flatFlags = (node) => {
const flags = [];
const binaryExpressions = [node];
while (binaryExpressions.length > 0) {
const { left, right } = binaryExpressions.shift();
for (const node of [left, right]) {
if (node.type === "BinaryExpression" && node.operator === "|") {
binaryExpressions.push(node);
continue;
}
if (!isCommentCheckFlags(node)) {
return [];
}
flags.push(node);
}
}
return flags.map((node) => node.property.name);
};
module.exports = {
meta: {
type: "suggestion",
docs: {
url:
"https://github.com/prettier/prettier/blob/main/scripts/eslint-plugin-prettier-internal-rules/no-conflicting-comment-check-flags.js",
},
messages: {
[MESSAGE_ID_UNIQUE]: "Do not use same flag multiple times.",
[MESSAGE_ID_CONFLICTING]: "Do not use {{flags}} together.",
},
},
create(context) {
return {
':not(BinaryExpression) > BinaryExpression[operator="|"]'(node) {
const flags = flatFlags(node);
if (flags.length < 2) {
return;
}
const uniqueFlags = new Set(flags);
if (uniqueFlags.size !== flags.length) {
context.report({
node,
messageId: MESSAGE_ID_UNIQUE,
});
return;
}
for (const group of conflictingFlags) {
const presentFlags = group.filter((flag) => uniqueFlags.has(flag));
if (presentFlags.length > 1) {
context.report({
node,
messageId: MESSAGE_ID_CONFLICTING,
data: {
flags: presentFlags
.map((flag) => `'CommentCheckFlags.${flag}'`)
.join(", "),
},
});
}
}
},
};
},
};
@@ -166,6 +166,69 @@ test("jsx-identifier-case", {
],
});
test("no-conflicting-comment-check-flags", {
valid: [
"CommentCheckFlags.Leading",
"NotCommentCheckFlags.Leading | NotCommentCheckFlags.Trailing",
"CommentCheckFlags.Leading | CommentCheckFlags.Trailing | SOMETHING_ELSE",
"CommentCheckFlags.Leading & CommentCheckFlags.Trailing",
],
invalid: [
{
code: "CommentCheckFlags.Leading | CommentCheckFlags.Trailing",
output: null,
errors: [
{
message:
"Do not use 'CommentCheckFlags.Leading', 'CommentCheckFlags.Trailing' together.",
},
],
},
{
code:
"(CommentCheckFlags.Leading | CommentCheckFlags.Trailing) | CommentCheckFlags.Dangling",
output: null,
errors: [
{
message:
"Do not use 'CommentCheckFlags.Leading', 'CommentCheckFlags.Trailing', 'CommentCheckFlags.Dangling' together.",
},
],
},
{
code:
"CommentCheckFlags.Leading | CommentCheckFlags.Trailing | CommentCheckFlags.UNKNOWN",
output: null,
errors: [
{
message:
"Do not use 'CommentCheckFlags.Leading', 'CommentCheckFlags.Trailing' together.",
},
],
},
{
code:
"CommentCheckFlags.Block | CommentCheckFlags.Line | CommentCheckFlags.UNKNOWN",
output: null,
errors: [
{
message:
"Do not use 'CommentCheckFlags.Block', 'CommentCheckFlags.Line' together.",
},
],
},
{
code: "CommentCheckFlags.Block | CommentCheckFlags.Block",
output: null,
errors: [
{
message: "Do not use same flag multiple times.",
},
],
},
],
});
test("no-doc-builder-concat", {
valid: ["notConcat([])", "concat", "[].concat([])"],
invalid: [
+15
View File
@@ -110,6 +110,21 @@ class AstPath {
return result;
}
/**
* @param {() => void} callback
* @internal Unstable API. Don't use in plugins for now.
*/
try(callback) {
const { stack } = this;
const stackBackup = [...stack];
try {
return callback();
} finally {
stack.length = 0;
stack.push(...stackBackup);
}
}
/**
* @param {...(
* | ((node: any, name: string | null, number: number | null) => boolean)
+2
View File
@@ -3,9 +3,11 @@
class ConfigError extends Error {}
class DebugError extends Error {}
class UndefinedParserError extends Error {}
class ArgExpansionBailout extends Error {}
module.exports = {
ConfigError,
DebugError,
UndefinedParserError,
ArgExpansionBailout,
};
+42 -15
View File
@@ -71,26 +71,53 @@ function traverseDoc(doc, onEnter, onExit, shouldTraverseConditionalGroups) {
}
function mapDoc(doc, cb) {
if (Array.isArray(doc)) {
return cb(doc.map((part) => mapDoc(part, cb)));
// Within a doc tree, the same subtrees can be found multiple times.
// E.g., often this happens in conditional groups.
// As an optimization (those subtrees can be huge) and to maintain the
// reference structure of the tree, the mapping results are cached in
// a map and reused.
const mapped = new Map();
return rec(doc);
function rec(doc) {
if (mapped.has(doc)) {
return mapped.get(doc);
}
const result = process(doc);
mapped.set(doc, result);
return result;
}
if (doc.type === "concat" || doc.type === "fill") {
const parts = doc.parts.map((part) => mapDoc(part, cb));
return cb({ ...doc, parts });
}
function process(doc) {
if (Array.isArray(doc)) {
return cb(doc.map(rec));
}
if (doc.type === "if-break") {
const breakContents = doc.breakContents && mapDoc(doc.breakContents, cb);
const flatContents = doc.flatContents && mapDoc(doc.flatContents, cb);
return cb({ ...doc, breakContents, flatContents });
}
if (doc.type === "concat" || doc.type === "fill") {
const parts = doc.parts.map(rec);
return cb({ ...doc, parts });
}
if (doc.contents) {
const contents = mapDoc(doc.contents, cb);
return cb({ ...doc, contents });
if (doc.type === "if-break") {
const breakContents = doc.breakContents && rec(doc.breakContents);
const flatContents = doc.flatContents && rec(doc.flatContents);
return cb({ ...doc, breakContents, flatContents });
}
if (doc.type === "group" && doc.expandedStates) {
const expandedStates = doc.expandedStates.map(rec);
const contents = expandedStates[0];
return cb({ ...doc, contents, expandedStates });
}
if (doc.contents) {
const contents = rec(doc.contents);
return cb({ ...doc, contents });
}
return cb(doc);
}
return cb(doc);
}
function findInDoc(doc, fn, defaultValue) {
+1 -1
View File
@@ -534,7 +534,7 @@ function parseNestedCSS(node, options) {
// `@color :blue;`
if (
!["page", "nest"].includes(node.name) &&
!["page", "nest", "keyframes"].includes(node.name) &&
node.params &&
node.params[0] === ":"
) {
+3
View File
@@ -39,6 +39,9 @@ function genericPrint(path, options, print) {
return [
hasOperation ? node.operation : "",
hasOperation && hasName ? [" ", print("name")] : "",
hasOperation && !hasName && isNonEmptyArray(node.variableDefinitions)
? " "
: "",
isNonEmptyArray(node.variableDefinitions)
? group([
"(",
+6 -28
View File
@@ -121,27 +121,13 @@ function print(path, options, print) {
}
case "ElementModifierStatement": {
return group(["{{", printPathAndParams(path, print), softline, "}}"]);
return group(["{{", printPathAndParams(path, print), "}}"]);
}
case "MustacheStatement": {
const isParentOfSpecifiedTypes = isParentOfSomeType(path, [
"AttrNode",
"ConcatStatement",
]);
const isChildOfElementNodeAndDoesNotHaveParams =
isParentOfSomeType(path, ["ElementNode"]) &&
doesNotHaveHashParams(node) &&
doesNotHavePositionalParams(node);
const shouldBreakOpeningMustache =
isParentOfSpecifiedTypes || isChildOfElementNodeAndDoesNotHaveParams;
return group([
printOpeningMustache(node),
shouldBreakOpeningMustache ? indent(softline) : "",
printPathAndParams(path, print),
softline,
printClosingMustache(node),
]);
}
@@ -550,7 +536,7 @@ function printOpenBlock(path, print) {
return group([
openingMustache,
indent(group(attributes)),
indent(attributes),
softline,
closingMustache,
]);
@@ -570,8 +556,8 @@ function printElseIfBlock(path, print) {
return [
printInverseBlockOpeningMustache(parentNode),
"else ",
printPathAndParams(path, print),
"else if ",
printParams(path, print),
printInverseBlockClosingMustache(parentNode),
];
}
@@ -764,7 +750,7 @@ function printPathAndParams(path, print) {
return p;
}
return indent(group([p, line, params]));
return [indent([p, line, params]), softline];
}
function printPath(path, print) {
@@ -796,14 +782,6 @@ function printBlockParams(node) {
return ["as |", node.blockParams.join(" "), "|"];
}
function doesNotHaveHashParams(node) {
return node.hash.pairs.length === 0;
}
function doesNotHavePositionalParams(node) {
return node.params.length === 0;
}
module.exports = {
print,
massageAstNode: clean,
+39
View File
@@ -0,0 +1,39 @@
import "angular-html-parser/lib/compiler/src/ml_parser/ast";
import { HtmlTagDefinition } from "angular-html-parser/lib/compiler/src/ml_parser/html_tags";
declare module "angular-html-parser/lib/compiler/src/ml_parser/ast" {
interface Attribute {
startSourceSpan: never;
endSourceSpan: never;
// see restoreName in parser-html.js
namespace?: string | null;
hasExplicitNamespace?: boolean;
}
interface CDATA {
startSourceSpan: never;
endSourceSpan: never;
}
interface Comment {
startSourceSpan: never;
endSourceSpan: never;
}
interface DocType {
startSourceSpan: never;
endSourceSpan: never;
}
interface Element {
tagDefinition: HtmlTagDefinition;
// see restoreName in parser-html.js
namespace?: string | null;
hasExplicitNamespace?: boolean;
}
interface Text {
startSourceSpan: never;
endSourceSpan: never;
}
}
+13
View File
@@ -3,11 +3,15 @@
const fromPairs = require("lodash/fromPairs");
const { isNonEmptyArray } = require("../common/util");
const getLast = require("../utils/get-last");
const NODES_KEYS = {
attrs: true,
children: true,
};
// TODO: typechecking is problematic for this class because of this issue:
// https://github.com/microsoft/TypeScript/issues/26811
class Node {
constructor(props = {}) {
for (const [key, value] of Object.entries(props)) {
@@ -33,6 +37,7 @@ class Node {
}
map(fn) {
/** @type{any} */
let newNode = null;
for (const NODES_KEY in NODES_KEYS) {
@@ -54,6 +59,7 @@ class Node {
newNode[key] = this[key];
}
}
// @ts-ignore
const { index, siblings, prev, next, parent } = this;
setNonEnumerableProperties(newNode, {
index,
@@ -67,23 +73,30 @@ class Node {
return fn(newNode || this);
}
/**
* @param {Object} [overrides]
*/
clone(overrides) {
return new Node(overrides ? { ...this, ...overrides } : this);
}
get firstChild() {
// @ts-ignore
return isNonEmptyArray(this.children) ? this.children[0] : null;
}
get lastChild() {
// @ts-ignore
return isNonEmptyArray(this.children) ? getLast(this.children) : null;
}
// for element and attribute
get rawName() {
// @ts-ignore
return this.hasExplicitNamespace ? this.fullName : this.name;
}
get fullName() {
// @ts-ignore
return this.namespace ? this.namespace + ":" + this.name : this.name;
}
}
+19 -14
View File
@@ -6,24 +6,29 @@ const {
// https://css-tricks.com/how-to-create-an-ie-only-stylesheet
// <!--[if ... ]> ... <![endif]-->
const IE_CONDITIONAL_START_END_COMMENT_REGEX = /^(\[if([^\]]*?)]>)([\S\s]*?)<!\s*\[endif]$/;
// <!--[if ... ]><!-->
const IE_CONDITIONAL_START_COMMENT_REGEX = /^\[if([^\]]*?)]><!$/;
// <!--<![endif]-->
const IE_CONDITIONAL_END_COMMENT_REGEX = /^<!\s*\[endif]$/;
const REGEX_PARSE_TUPLES = [
[IE_CONDITIONAL_START_END_COMMENT_REGEX, parseIeConditionalStartEndComment],
[IE_CONDITIONAL_START_COMMENT_REGEX, parseIeConditionalStartComment],
[IE_CONDITIONAL_END_COMMENT_REGEX, parseIeConditionalEndComment],
const parseFunctions = [
{
// <!--[if ... ]> ... <![endif]-->
regex: /^(\[if([^\]]*?)]>)(.*?)<!\s*\[endif]$/s,
parse: parseIeConditionalStartEndComment,
},
{
// <!--[if ... ]><!-->
regex: /^\[if([^\]]*?)]><!$/,
parse: parseIeConditionalStartComment,
},
{
// <!--<![endif]-->
regex: /^<!\s*\[endif]$/,
parse: parseIeConditionalEndComment,
},
];
function parseIeConditionalComment(node, parseHtml) {
if (node.value) {
let match;
for (const [regex, parse] of REGEX_PARSE_TUPLES) {
if ((match = node.value.match(regex))) {
for (const { regex, parse } of parseFunctions) {
const match = node.value.match(regex);
if (match) {
return parse(node, parseHtml, match);
}
}
+117 -99
View File
@@ -19,6 +19,27 @@ const { Node } = require("./ast");
const { parseIeConditionalComment } = require("./conditional-comment");
const { locStart, locEnd } = require("./loc");
/**
* @typedef {import('angular-html-parser/lib/compiler/src/ml_parser/ast').Node} AstNode
* @typedef {import('angular-html-parser/lib/compiler/src/ml_parser/ast').Attribute} Attribute
* @typedef {import('angular-html-parser/lib/compiler/src/ml_parser/ast').Element} Element
* @typedef {import('angular-html-parser/lib/compiler/src/ml_parser/parser').ParseTreeResult} ParserTreeResult
* @typedef {Omit<import('angular-html-parser').ParseOptions, 'canSelfClose'> & {
* recognizeSelfClosing?: boolean;
* normalizeTagName?: boolean;
* normalizeAttributeName?: boolean;
* }} ParserOptions
* @typedef {{
* parser: 'html' | 'angular' | 'vue' | 'lwc',
* filepath?: string
* }} Options
*/
/**
* @param {string} input
* @param {ParserOptions} parserOptions
* @param {Options} options
*/
function ngHtmlParser(
input,
{
@@ -35,12 +56,6 @@ function ngHtmlParser(
const {
RecursiveVisitor,
visitAll,
Attribute,
CDATA,
Comment,
DocType,
Element,
Text,
} = require("angular-html-parser/lib/compiler/src/ml_parser/ast");
const {
ParseSourceSpan,
@@ -56,82 +71,85 @@ function ngHtmlParser(
getTagContentType,
});
const isVueHtml =
options.parser === "vue" &&
rootNodes.some(
if (options.parser === "vue") {
const isVueHtml = rootNodes.some(
(node) =>
(node instanceof DocType && node.value === "html") ||
(node instanceof Element && node.name.toLowerCase() === "html")
(node.type === "docType" && node.value === "html") ||
(node.type === "element" && node.name.toLowerCase() === "html")
);
if (options.parser === "vue" && !isVueHtml) {
const shouldParseAsHTML = (node) => {
/* istanbul ignore next */
if (!node) {
return false;
}
if (node.name !== "template") {
return false;
}
const langAttr = node.attrs.find((attr) => attr.name === "lang");
const langValue = langAttr && langAttr.value;
return !langValue || inferParserByLanguage(langValue, options) === "html";
};
if (rootNodes.some(shouldParseAsHTML)) {
let secondParseResult;
const doSecondParse = () =>
parser.parse(input, {
canSelfClose: recognizeSelfClosing,
allowHtmComponentClosingTags,
isTagNameCaseSensitive,
});
const getSecondParse = () =>
secondParseResult || (secondParseResult = doSecondParse());
const getSameLocationNode = (node) =>
getSecondParse().rootNodes.find(
({ startSourceSpan }) =>
startSourceSpan &&
startSourceSpan.start.offset === node.startSourceSpan.start.offset
if (!isVueHtml) {
const shouldParseAsHTML = (/** @type {AstNode} */ node) => {
/* istanbul ignore next */
if (!node) {
return false;
}
if (node.type !== "element" || node.name !== "template") {
return false;
}
const langAttr = node.attrs.find((attr) => attr.name === "lang");
const langValue = langAttr && langAttr.value;
return (
!langValue || inferParserByLanguage(langValue, options) === "html"
);
for (let i = 0; i < rootNodes.length; i++) {
const node = rootNodes[i];
const { endSourceSpan, startSourceSpan } = node;
const isUnclosedNode = endSourceSpan === null;
if (isUnclosedNode) {
const result = getSecondParse();
errors = result.errors;
rootNodes[i] = getSameLocationNode(node) || node;
} else if (shouldParseAsHTML(node)) {
const result = getSecondParse();
const startOffset = startSourceSpan.end.offset;
const endOffset = endSourceSpan.start.offset;
for (const error of result.errors) {
const { offset } = error.span.start;
/* istanbul ignore next */
if (startOffset < offset && offset < endOffset) {
errors = [error];
break;
};
if (rootNodes.some(shouldParseAsHTML)) {
/** @type {ParserTreeResult | undefined} */
let secondParseResult;
const doSecondParse = () =>
parser.parse(input, {
canSelfClose: recognizeSelfClosing,
allowHtmComponentClosingTags,
isTagNameCaseSensitive,
});
const getSecondParse = () =>
secondParseResult || (secondParseResult = doSecondParse());
const getSameLocationNode = (node) =>
getSecondParse().rootNodes.find(
({ startSourceSpan }) =>
startSourceSpan &&
startSourceSpan.start.offset === node.startSourceSpan.start.offset
);
for (let i = 0; i < rootNodes.length; i++) {
const node = rootNodes[i];
const { endSourceSpan, startSourceSpan } = node;
const isUnclosedNode = endSourceSpan === null;
if (isUnclosedNode) {
const result = getSecondParse();
errors = result.errors;
rootNodes[i] = getSameLocationNode(node) || node;
} else if (shouldParseAsHTML(node)) {
const result = getSecondParse();
const startOffset = startSourceSpan.end.offset;
const endOffset = endSourceSpan.start.offset;
for (const error of result.errors) {
const { offset } = error.span.start;
/* istanbul ignore next */
if (startOffset < offset && offset < endOffset) {
errors = [error];
break;
}
}
rootNodes[i] = getSameLocationNode(node) || node;
}
rootNodes[i] = getSameLocationNode(node) || node;
}
}
}
} else if (isVueHtml) {
// If not Vue SFC, treat as html
recognizeSelfClosing = true;
normalizeTagName = true;
normalizeAttributeName = true;
allowHtmComponentClosingTags = true;
isTagNameCaseSensitive = false;
const htmlParseResult = parser.parse(input, {
canSelfClose: recognizeSelfClosing,
allowHtmComponentClosingTags,
isTagNameCaseSensitive,
});
} else {
// If not Vue SFC, treat as html
recognizeSelfClosing = true;
normalizeTagName = true;
normalizeAttributeName = true;
allowHtmComponentClosingTags = true;
isTagNameCaseSensitive = false;
const htmlParseResult = parser.parse(input, {
canSelfClose: recognizeSelfClosing,
allowHtmComponentClosingTags,
isTagNameCaseSensitive,
});
rootNodes = htmlParseResult.rootNodes;
errors = htmlParseResult.errors;
rootNodes = htmlParseResult.rootNodes;
errors = htmlParseResult.errors;
}
}
if (errors.length > 0) {
@@ -145,31 +163,16 @@ function ngHtmlParser(
});
}
const addType = (node) => {
if (node instanceof Attribute) {
node.type = "attribute";
} else if (node instanceof CDATA) {
node.type = "cdata";
} else if (node instanceof Comment) {
node.type = "comment";
} else if (node instanceof DocType) {
node.type = "docType";
} else if (node instanceof Element) {
node.type = "element";
} else if (node instanceof Text) {
node.type = "text";
} else {
/* istanbul ignore next */
throw new Error(`Unexpected node ${JSON.stringify(node)}`);
}
};
/**
* @param {Attribute | Element} node
*/
const restoreName = (node) => {
const namespace = node.name.startsWith(":")
? node.name.slice(1).split(":")[0]
: null;
const rawName = node.nameSpan.toString();
const hasExplicitNamespace = rawName.startsWith(`${namespace}:`);
const hasExplicitNamespace =
namespace !== null && rawName.startsWith(`${namespace}:`);
const name = hasExplicitNamespace
? rawName.slice(namespace.length + 1)
: rawName;
@@ -179,8 +182,11 @@ function ngHtmlParser(
node.hasExplicitNamespace = hasExplicitNamespace;
};
/**
* @param {AstNode} node
*/
const restoreNameAndValue = (node) => {
if (node instanceof Element) {
if (node.type === "element") {
restoreName(node);
for (const attr of node.attrs) {
restoreName(attr);
@@ -193,11 +199,11 @@ function ngHtmlParser(
}
}
}
} else if (node instanceof Comment) {
} else if (node.type === "comment") {
node.value = node.sourceSpan
.toString()
.slice("<!--".length, -"-->".length);
} else if (node instanceof Text) {
} else if (node.type === "text") {
node.value = node.sourceSpan.toString();
}
};
@@ -207,7 +213,7 @@ function ngHtmlParser(
return fn(lowerCasedText) ? lowerCasedText : text;
};
const normalizeName = (node) => {
if (node instanceof Element) {
if (node.type === "element") {
if (
normalizeTagName &&
(!node.namespace ||
@@ -247,8 +253,11 @@ function ngHtmlParser(
}
};
/**
* @param {AstNode} node
*/
const addTagDefinition = (node) => {
if (node instanceof Element) {
if (node.type === "element") {
const tagDefinition = getHtmlTagDefinition(
isTagNameCaseSensitive ? node.name : node.name.toLowerCase()
);
@@ -267,7 +276,6 @@ function ngHtmlParser(
visitAll(
new (class extends RecursiveVisitor {
visit(node) {
addType(node);
restoreNameAndValue(node);
addTagDefinition(node);
normalizeName(node);
@@ -280,6 +288,12 @@ function ngHtmlParser(
return rootNodes;
}
/**
* @param {string} text
* @param {Options} options
* @param {ParserOptions} parserOptions
* @param {boolean} shouldParseFrontMatter
*/
function _parse(text, options, parserOptions, shouldParseFrontMatter = true) {
const { frontMatter, content } = shouldParseFrontMatter
? parseFrontMatter(text)
@@ -298,6 +312,7 @@ function _parse(text, options, parserOptions, shouldParseFrontMatter = true) {
const start = new ParseLocation(file, 0, 0, 0);
const end = start.moveBy(frontMatter.raw.length);
frontMatter.sourceSpan = new ParseSourceSpan(start, end);
// @ts-ignore
rawAst.children.unshift(frontMatter);
}
@@ -346,6 +361,9 @@ function _parse(text, options, parserOptions, shouldParseFrontMatter = true) {
});
}
/**
* @param {ParserOptions} parserOptions
*/
function createParser({
recognizeSelfClosing = false,
normalizeTagName = false,
+21 -35
View File
@@ -1,6 +1,11 @@
"use strict";
/**
* @typedef {import("../document").Doc} Doc
*/
const assert = require("assert");
const {
builders: {
breakParent,
@@ -676,6 +681,7 @@ function printAttributes(path, options, print) {
node.attrs[0].fullName === "src" &&
node.children.length === 0;
/** @type {Doc[]} */
const parts = [
indent([
forceNotToBreakAttrContent ? " " : line,
@@ -992,7 +998,7 @@ function printEmbeddedAttributeValue(node, originalTextToDoc, options) {
group([indent([softline, doc]), canHaveTrailingWhitespace ? softline : ""]);
const printMaybeHug = (doc) => (shouldHug ? printHug(doc) : printExpand(doc));
const textToDoc = (code, opts) =>
const attributeTextToDoc = (code, opts) =>
originalTextToDoc(
code,
{ __onHtmlBindingRoot, __embeddedInHtml: true, ...opts },
@@ -1017,25 +1023,21 @@ function printEmbeddedAttributeValue(node, originalTextToDoc, options) {
const value = getValue();
if (!value.includes("{{")) {
return printExpand(
textToDoc(
value,
{
parser: "css",
__isHTMLStyleAttribute: true,
},
{ stripTrailingHardline: true }
)
attributeTextToDoc(value, {
parser: "css",
__isHTMLStyleAttribute: true,
})
);
}
}
if (options.parser === "vue") {
if (node.fullName === "v-for") {
return printVueFor(getValue(), textToDoc);
return printVueFor(getValue(), attributeTextToDoc);
}
if (isVueSlotAttribute(node) || isVueSfcBindingsAttribute(node, options)) {
return printVueBindings(getValue(), textToDoc);
return printVueBindings(getValue(), attributeTextToDoc);
}
/**
@@ -1058,35 +1060,23 @@ function printEmbeddedAttributeValue(node, originalTextToDoc, options) {
if (isKeyMatched(vueEventBindingPatterns)) {
const value = getValue();
return printMaybeHug(
textToDoc(
value,
{
parser: isVueEventBindingExpression(value)
? "__js_expression"
: "__vue_event_binding",
},
{ stripTrailingHardline: true }
)
attributeTextToDoc(value, {
parser: isVueEventBindingExpression(value)
? "__js_expression"
: "__vue_event_binding",
})
);
}
if (isKeyMatched(vueExpressionBindingPatterns)) {
return printMaybeHug(
textToDoc(
getValue(),
{ parser: "__vue_expression" },
{ stripTrailingHardline: true }
)
attributeTextToDoc(getValue(), { parser: "__vue_expression" })
);
}
if (isKeyMatched(jsExpressionBindingPatterns)) {
return printMaybeHug(
textToDoc(
getValue(),
{ parser: "__js_expression" },
{ stripTrailingHardline: true }
)
attributeTextToDoc(getValue(), { parser: "__js_expression" })
);
}
}
@@ -1094,11 +1084,7 @@ function printEmbeddedAttributeValue(node, originalTextToDoc, options) {
if (options.parser === "angular") {
const ngTextToDoc = (code, opts) =>
// angular does not allow trailing comma
textToDoc(
code,
{ ...opts, trailingComma: "none" },
{ stripTrailingHardline: true }
);
attributeTextToDoc(code, { ...opts, trailingComma: "none" });
/**
* *directive="angularDirective"
+8
View File
@@ -1,5 +1,9 @@
"use strict";
/**
* @typedef {import("../common/ast-path")} AstPath
*/
const htmlTagNames = require("html-tag-names");
const htmlElementAttributes = require("html-element-attributes");
const { inferParserByLanguage, isFrontMatterNode } = require("../common/util");
@@ -465,6 +469,10 @@ function isPreLikeNode(node) {
return getNodeCssStyleWhiteSpace(node).startsWith("pre");
}
/**
* @param {AstPath} path
* @param {(any) => boolean} predicate
*/
function countParents(path, predicate) {
let counter = 0;
for (let i = path.stack.length - 1; i >= 0; i--) {
+5 -5
View File
@@ -163,10 +163,6 @@ function needsParens(path, options) {
}
switch (node.type) {
case "SpreadElement":
case "SpreadProperty":
return name === "object" && parent.type === "MemberExpression";
case "UpdateExpression":
if (parent.type === "UnaryExpression") {
return (
@@ -237,6 +233,10 @@ function needsParens(path, options) {
case "TSAsExpression":
case "LogicalExpression":
switch (parent.type) {
case "TSAsExpression":
// example: foo as unknown as Bar
return node.type !== "TSAsExpression";
case "ConditionalExpression":
return node.type === "TSAsExpression";
@@ -257,7 +257,6 @@ function needsParens(path, options) {
case "SpreadProperty":
case "BindExpression":
case "AwaitExpression":
case "TSAsExpression":
case "TSNonNullExpression":
case "UpdateExpression":
return true;
@@ -267,6 +266,7 @@ function needsParens(path, options) {
return name === "object";
case "AssignmentExpression":
case "AssignmentPattern":
return (
name === "left" &&
(node.type === "TSTypeAssertion" || node.type === "TSAsExpression")
-6
View File
@@ -150,11 +150,6 @@ const allowedMessages = new Set([
"'with' in strict mode",
"Legacy octal literals are not allowed in strict mode",
"Invalid left-hand side in parenthesized expression",
"Invalid left-hand side in assignment expression",
"Invalid left-hand side in postfix operation",
"Invalid left-hand side in prefix operation",
"Type argument list cannot be empty.",
"Type parameter list cannot be empty.",
"Type parameters cannot appear on a constructor declaration.",
@@ -185,7 +180,6 @@ const allowedMessages = new Set([
"Unexpected trailing comma after rest element",
"Decorators cannot be used to decorate parameters",
"Unterminated JSX contents",
"Invalid parenthesized assignment pattern",
'Unexpected token, expected "}"',
"Unexpected token :",
"Unexpected reserved word 'package'",
+140 -50
View File
@@ -9,13 +9,17 @@ const {
hasLeadingOwnLineComment,
isBinaryish,
isStringLiteral,
isLiteral,
isNumericLiteral,
isCallExpression,
isMemberExpression,
getCallArguments,
isSimpleCallArgument,
rawText,
hasComment,
isSignedNumericLiteral,
} = require("../utils");
const { shouldInlineLogicalExpression } = require("./binaryish");
const { printCallExpression } = require("./call-expression");
function printAssignment(
path,
@@ -25,7 +29,7 @@ function printAssignment(
operator,
rightPropertyName
) {
const layout = chooseLayout(path, options, leftDoc, rightPropertyName);
const layout = chooseLayout(path, options, print, leftDoc, rightPropertyName);
const rightDoc = print(rightPropertyName, { assignmentLayout: layout });
@@ -49,6 +53,9 @@ function printAssignment(
]);
}
case "break-lhs":
return group([leftDoc, operator, " ", group(rightDoc)]);
// Parts of assignment chains aren't wrapped in groups.
// Once one of them breaks, the chain breaks too.
case "chain":
@@ -81,7 +88,7 @@ function printVariableDeclarator(path, options, print) {
return printAssignment(path, options, print, print("id"), " =", "init");
}
function chooseLayout(path, options, leftDoc, rightPropertyName) {
function chooseLayout(path, options, print, leftDoc, rightPropertyName) {
const node = path.getValue();
const rightNode = node[rightPropertyName];
@@ -96,7 +103,7 @@ function chooseLayout(path, options, leftDoc, rightPropertyName) {
const isTail = !isAssignment(rightNode);
const shouldUseChainFormatting = path.match(
isAssignment,
(node) => isAssignment(node) || node.type === "VariableDeclarator",
isAssignmentOrVariableDeclarator,
(node) =>
!isTail ||
(node.type !== "ExpressionStatement" &&
@@ -119,26 +126,49 @@ function chooseLayout(path, options, leftDoc, rightPropertyName) {
return "break-after-operator";
}
// do not put values on a separate line from the key in json
if (options.parser === "json5" || options.parser === "json") {
if (
(rightNode.type === "CallExpression" &&
rightNode.callee.name === "require") ||
// do not put values on a separate line from the key in json
options.parser === "json5" ||
options.parser === "json"
) {
return "never-break-after-operator";
}
if (isComplexDestructuring(node)) {
return "break-lhs";
}
// wrapping object properties with very short keys usually doesn't add much value
const hasShortKey = isObjectPropertyWithShortKey(node, leftDoc, options);
if (shouldBreakAfterOperator(rightNode, hasShortKey)) {
if (
path.call(
() => shouldBreakAfterOperator(path, options, print, hasShortKey),
rightPropertyName
)
) {
return "break-after-operator";
}
if (hasShortKey || shouldNeverBreakAfterOperator(rightNode)) {
if (
hasShortKey ||
rightNode.type === "TemplateLiteral" ||
rightNode.type === "TaggedTemplateExpression" ||
rightNode.type === "BooleanLiteral" ||
isNumericLiteral(rightNode) ||
rightNode.type === "ClassExpression"
) {
return "never-break-after-operator";
}
return "fluid";
}
function shouldBreakAfterOperator(rightNode, hasShortKey) {
function shouldBreakAfterOperator(path, options, print, hasShortKey) {
const rightNode = path.getValue();
if (isBinaryish(rightNode) && !shouldInlineLogicalExpression(rightNode)) {
return true;
}
@@ -160,22 +190,24 @@ function shouldBreakAfterOperator(rightNode, hasShortKey) {
}
let node = rightNode;
const propertiesForPath = [];
for (;;) {
if (node.type === "UnaryExpression") {
node = node.argument;
propertiesForPath.push("argument");
} else if (node.type === "TSNonNullExpression") {
node = node.expression;
propertiesForPath.push("expression");
} else {
break;
}
}
if (
isStringLiteral(node) ||
isMemberExpressionChainWithSimpleCalls(node) ||
(isSimpleCall(node) &&
isSimpleCall(node.callee) &&
(isMemberExpressionChainHead(node.callee.callee) ||
isMemberExpressionChainWithSimpleCalls(node.callee.callee)))
path.call(
() => isPoorlyBreakableMemberOrCallChain(path, options, print),
...propertiesForPath
)
) {
return true;
}
@@ -183,57 +215,115 @@ function shouldBreakAfterOperator(rightNode, hasShortKey) {
return false;
}
function shouldNeverBreakAfterOperator(rightNode) {
return (
rightNode.type === "TemplateLiteral" ||
rightNode.type === "TaggedTemplateExpression" ||
rightNode.type === "BooleanLiteral" ||
isNumericLiteral(rightNode) ||
(rightNode.type === "CallExpression" &&
rightNode.callee.name === "require") ||
rightNode.type === "ClassExpression"
);
// prefer to break destructuring assignment
// if it includes default values or non-shorthand properties
function isComplexDestructuring(node) {
if (isAssignmentOrVariableDeclarator(node)) {
const leftNode = node.left || node.id;
return (
leftNode.type === "ObjectPattern" &&
leftNode.properties.length > 2 &&
leftNode.properties.some(
(property) =>
(property.type === "ObjectProperty" ||
property.type === "Property") &&
(!property.shorthand ||
(property.value && property.value.type === "AssignmentPattern"))
)
);
}
return false;
}
function isAssignment(node) {
return node.type === "AssignmentExpression";
}
function isMemberExpressionChainHead(node) {
return node.type === "Identifier" || node.type === "ThisExpression";
function isAssignmentOrVariableDeclarator(node) {
return isAssignment(node) || node.type === "VariableDeclarator";
}
function isMemberExpressionChainWithSimpleCalls(node) {
if (!isMemberExpression(node)) {
return false;
/**
* A chain with no calls at all or whose calls are all without arguments or with lone short arguments,
* excluding chains printed by `printMemberChain`
*/
function isPoorlyBreakableMemberOrCallChain(
path,
options,
print,
deep = false
) {
const node = path.getValue();
const goDeeper = () =>
isPoorlyBreakableMemberOrCallChain(path, options, print, true);
if (node.type === "TSNonNullExpression") {
return path.call(goDeeper, "expression");
}
let { object } = node;
for (;;) {
if (object.type === "TSNonNullExpression") {
object = object.expression;
} else if (isCallExpression(object)) {
if (!isSimpleCall(object)) {
return false;
}
object = object.callee;
} else {
break;
if (isCallExpression(node)) {
/** @type {any} TODO */
const doc = printCallExpression(path, options, print);
if (doc.label === "member-chain") {
return false;
}
const args = getCallArguments(node);
const isPoorlyBreakableCall =
args.length === 0 ||
(args.length === 1 && isLoneShortArgument(args[0], options));
if (!isPoorlyBreakableCall) {
return false;
}
return path.call(goDeeper, "callee");
}
return (
isMemberExpressionChainHead(object) ||
isMemberExpressionChainWithSimpleCalls(object)
);
if (isMemberExpression(node)) {
return path.call(goDeeper, "object");
}
return deep && (node.type === "Identifier" || node.type === "ThisExpression");
}
function isSimpleCall(node) {
if (!isCallExpression(node)) {
const LONE_SHORT_ARGUMENT_THRESHOLD_RATE = 0.25;
function isLoneShortArgument(node, { printWidth }) {
if (hasComment(node)) {
return false;
}
const args = getCallArguments(node);
return (
args.length === 0 || (args.length === 1 && isSimpleCallArgument(args[0], 1))
);
const threshold = printWidth * LONE_SHORT_ARGUMENT_THRESHOLD_RATE;
if (
node.type === "ThisExpression" ||
(node.type === "Identifier" && node.name.length <= threshold) ||
(isSignedNumericLiteral(node) && !hasComment(node.argument))
) {
return true;
}
const regexpPattern =
(node.type === "Literal" && "regex" in node && node.regex.pattern) ||
(node.type === "RegExpLiteral" && node.pattern);
if (regexpPattern) {
return regexpPattern.length <= threshold;
}
if (isStringLiteral(node)) {
return rawText(node).length <= threshold;
}
if (node.type === "TemplateLiteral") {
return (
node.expressions.length === 0 &&
node.quasis[0].value.raw.length <= threshold &&
!node.quasis[0].value.raw.includes("\n")
);
}
return isLiteral(node);
}
function isObjectPropertyWithShortKey(node, keyDoc, options) {
+81 -90
View File
@@ -4,7 +4,6 @@ const { printDanglingComments } = require("../../main/comments");
const { getLast, getPenultimate } = require("../../common/util");
const {
getFunctionParameters,
iterateFunctionParametersPath,
hasComment,
CommentCheckFlags,
isFunctionCompositionArgs,
@@ -31,6 +30,7 @@ const {
utils: { willBreak },
} = require("../../document");
const { ArgExpansionBailout } = require("../../common/errors");
const { isConciselyPrintedArray } = require("./array");
function printCallArguments(path, options, print) {
@@ -51,34 +51,7 @@ function printCallArguments(path, options, print) {
return ["(", print(["arguments", 0]), ", ", print(["arguments", 1]), ")"];
}
// func(
// ({
// a,
//
// b
// }) => {}
// );
function shouldBreakForArrowFunctionInArguments(arg, argPath) {
if (
!arg ||
arg.type !== "ArrowFunctionExpression" ||
!arg.body ||
arg.body.type !== "BlockStatement" ||
getFunctionParameters(arg).length === 0
) {
return false;
}
let shouldBreak = false;
iterateFunctionParametersPath(argPath, () => {
shouldBreak = shouldBreak || willBreak(print());
});
return shouldBreak;
}
let anyArgEmptyLine = false;
let shouldBreakForArrowFunction = false;
let hasEmptyLineFollowingFirstArg = false;
const lastArgIndex = args.length - 1;
const printedArguments = [];
@@ -99,11 +72,6 @@ function printCallArguments(path, options, print) {
parts.push(",", line);
}
shouldBreakForArrowFunction = shouldBreakForArrowFunctionInArguments(
arg,
argPath
);
printedArguments.push(parts);
});
@@ -122,8 +90,9 @@ function printCallArguments(path, options, print) {
}
if (
path.getParentNode().type !== "Decorator" &&
isFunctionCompositionArgs(args)
anyArgEmptyLine ||
(path.getParentNode().type !== "Decorator" &&
isFunctionCompositionArgs(args))
) {
return allArgsBrokenOut();
}
@@ -131,67 +100,66 @@ function printCallArguments(path, options, print) {
const shouldGroupFirst = shouldGroupFirstArg(args);
const shouldGroupLast = shouldGroupLastArg(args, options);
if (shouldGroupFirst || shouldGroupLast) {
const shouldBreak =
(shouldGroupFirst
if (
shouldGroupFirst
? printedArguments.slice(1).some(willBreak)
: printedArguments.slice(0, -1).some(willBreak)) ||
anyArgEmptyLine ||
shouldBreakForArrowFunction;
: printedArguments.slice(0, -1).some(willBreak)
) {
return allArgsBrokenOut();
}
// We want to print the last argument with a special flag
let printedExpanded = [];
iterateCallArgumentsPath(path, (argPath, i) => {
if (shouldGroupFirst && i === 0) {
printedExpanded = [
[
print([], { expandFirstArg: true }),
printedArguments.length > 1 ? "," : "",
hasEmptyLineFollowingFirstArg ? hardline : line,
hasEmptyLineFollowingFirstArg ? hardline : "",
],
...printedArguments.slice(1),
];
}
if (shouldGroupLast && i === args.length - 1) {
printedExpanded = [
...printedArguments.slice(0, -1),
print([], { expandLastArg: true }),
];
}
});
const somePrintedArgumentsWillBreak = printedArguments.some(willBreak);
const simpleConcat = ["(", ...printedExpanded, ")"];
try {
path.try(() => {
iterateCallArgumentsPath(path, (argPath, i) => {
if (shouldGroupFirst && i === 0) {
printedExpanded = [
[
print([], { expandFirstArg: true }),
printedArguments.length > 1 ? "," : "",
hasEmptyLineFollowingFirstArg ? hardline : line,
hasEmptyLineFollowingFirstArg ? hardline : "",
],
...printedArguments.slice(1),
];
}
if (shouldGroupLast && i === lastArgIndex) {
printedExpanded = [
...printedArguments.slice(0, -1),
print([], { expandLastArg: true }),
];
}
});
});
} catch (caught) {
if (caught instanceof ArgExpansionBailout) {
return allArgsBrokenOut();
}
/* istanbul ignore next */
throw caught;
}
return [
somePrintedArgumentsWillBreak ? breakParent : "",
conditionalGroup(
[
!somePrintedArgumentsWillBreak &&
!node.typeArguments &&
!node.typeParameters
? simpleConcat
: ifBreak(allArgsBrokenOut(), simpleConcat),
shouldGroupFirst
? [
"(",
group(printedExpanded[0], { shouldBreak: true }),
...printedExpanded.slice(1),
")",
]
: [
"(",
...printedArguments.slice(0, -1),
group(getLast(printedExpanded), {
shouldBreak: true,
}),
")",
],
allArgsBrokenOut(),
],
{ shouldBreak }
),
printedArguments.some(willBreak) ? breakParent : "",
conditionalGroup([
["(", ...printedExpanded, ")"],
shouldGroupFirst
? [
"(",
group(printedExpanded[0], { shouldBreak: true }),
...printedExpanded.slice(1),
")",
]
: [
"(",
...printedArguments.slice(0, -1),
group(getLast(printedExpanded), { shouldBreak: true }),
")",
],
allArgsBrokenOut(),
]),
];
}
@@ -247,7 +215,9 @@ function couldGroupArg(arg, arrowChainRecursion = false) {
(!arrowChainRecursion &&
(isCallExpression(arg.body) ||
arg.body.type === "ConditionalExpression")) ||
isJsxNode(arg.body)))
isJsxNode(arg.body))) ||
arg.type === "DoExpression" ||
arg.type === "ModuleExpression"
);
}
@@ -279,6 +249,14 @@ function shouldGroupFirstArg(args) {
}
const [firstArg, secondArg] = args;
if (
firstArg.type === "ModuleExpression" &&
isTypeModuleObjectExpression(secondArg)
) {
return true;
}
return (
!hasComment(firstArg) &&
(firstArg.type === "FunctionExpression" ||
@@ -310,4 +288,17 @@ function isNonEmptyBlockStatement(node) {
);
}
// { type: "module" }
function isTypeModuleObjectExpression(node) {
return (
node.type === "ObjectExpression" &&
node.properties.length === 1 &&
node.properties[0].type === "ObjectProperty" &&
node.properties[0].key.type === "Identifier" &&
node.properties[0].key.name === "type" &&
node.properties[0].value.type === "StringLiteral" &&
node.properties[0].value.value === "module"
);
}
module.exports = printCallArguments;
+9 -15
View File
@@ -20,6 +20,7 @@ const {
isNextLineEmpty,
} = require("../utils");
const { locEnd } = require("../loc");
const { ArgExpansionBailout } = require("../../common/errors");
const { printFunctionTypeParameters } = require("./misc");
function printFunctionParameters(
@@ -57,8 +58,6 @@ function printFunctionParameters(
const parent = path.getParentNode();
const isParametersInTestCall = isTestCall(parent);
const shouldHugParameters = shouldHugFunctionParameters(functionNode);
const shouldExpandParameters =
expandArg && !parameters.some((node) => hasComment(node));
const printed = [];
iterateFunctionParametersPath(path, (parameterPath, index) => {
const isLastParameter = index === parameters.length - 1;
@@ -70,11 +69,7 @@ function printFunctionParameters(
return;
}
printed.push(",");
if (
isParametersInTestCall ||
shouldHugParameters ||
shouldExpandParameters
) {
if (isParametersInTestCall || shouldHugParameters) {
printed.push(" ");
} else if (isNextLineEmpty(parameters[index], options)) {
printed.push(hardline, hardline);
@@ -91,15 +86,14 @@ function printFunctionParameters(
// verylongcall( verylongcall((
// (a, b) => { a,
// } b,
// }) ) => {
// ) ) => {
// })
if (shouldExpandParameters) {
return group([
removeLines(typeParams),
"(",
...printed.map(removeLines),
")",
]);
if (expandArg) {
if (willBreak(typeParams) || willBreak(printed)) {
// Removing lines in this case leads to broken or ugly output
throw new ArgExpansionBailout();
}
return group([removeLines(typeParams), "(", removeLines(printed), ")"]);
}
// Single object destructuring should hug
+21 -9
View File
@@ -1,9 +1,13 @@
"use strict";
const {
builders: { softline, group, indent },
builders: { softline, group, indent, label },
} = require("../../document");
const { isNumericLiteral, isMemberExpression } = require("../utils");
const {
isNumericLiteral,
isMemberExpression,
isCallExpression,
} = require("../utils");
const { printOptionalToken } = require("./misc");
function printMemberExpression(path, options, print) {
@@ -21,6 +25,9 @@ function printMemberExpression(path, options, print) {
firstNonMemberParent.type === "TSNonNullExpression")
);
const objectDoc = print("object");
const lookupDoc = printMemberLookup(path, options, print);
const shouldInline =
(firstNonMemberParent &&
(firstNonMemberParent.type === "NewExpression" ||
@@ -30,14 +37,19 @@ function printMemberExpression(path, options, print) {
node.computed ||
(node.object.type === "Identifier" &&
node.property.type === "Identifier" &&
!isMemberExpression(parent));
!isMemberExpression(parent)) ||
((parent.type === "AssignmentExpression" ||
parent.type === "VariableDeclarator") &&
((isCallExpression(node.object) && node.object.arguments.length > 0) ||
(node.object.type === "TSNonNullExpression" &&
isCallExpression(node.object.expression) &&
node.object.expression.arguments.length > 0) ||
objectDoc.label === "member-chain"));
return [
print("object"),
shouldInline
? printMemberLookup(path, options, print)
: group(indent([softline, printMemberLookup(path, options, print)])),
];
return label(objectDoc.label === "member-chain" ? "member-chain" : "member", [
objectDoc,
shouldInline ? lookupDoc : group(indent([softline, lookupDoc])),
]);
}
function printMemberLookup(path, options, print) {
+31 -21
View File
@@ -41,11 +41,10 @@ function printObject(path, options, print) {
}
const isTypeAnnotation = node.type === "ObjectTypeAnnotation";
const fields = [];
const fields = [propertiesField];
if (isTypeAnnotation) {
fields.push("indexers", "callProperties", "internalSlots");
}
fields.push(propertiesField);
const firstProperty = fields
.map((field) => node[field][0])
@@ -109,26 +108,28 @@ function printObject(path, options, print) {
}, field);
}
if (fields.length > 1) {
propsAndLoc.sort((a, b) => a.loc - b.loc);
}
/** @type {Doc[]} */
let separatorParts = [];
const props = propsAndLoc
.sort((a, b) => a.loc - b.loc)
.map((prop) => {
const result = [...separatorParts, group(prop.printed)];
separatorParts = [separator, line];
if (
(prop.node.type === "TSPropertySignature" ||
prop.node.type === "TSMethodSignature" ||
prop.node.type === "TSConstructSignatureDeclaration") &&
hasComment(prop.node, CommentCheckFlags.PrettierIgnore)
) {
separatorParts.shift();
}
if (isNextLineEmpty(prop.node, options)) {
separatorParts.push(hardline);
}
return result;
});
const props = propsAndLoc.map((prop) => {
const result = [...separatorParts, group(prop.printed)];
separatorParts = [separator, line];
if (
(prop.node.type === "TSPropertySignature" ||
prop.node.type === "TSMethodSignature" ||
prop.node.type === "TSConstructSignatureDeclaration") &&
hasComment(prop.node, CommentCheckFlags.PrettierIgnore)
) {
separatorParts.shift();
}
if (isNextLineEmpty(prop.node, options)) {
separatorParts.push(hardline);
}
return result;
});
if (node.inexact) {
let printed;
@@ -225,7 +226,16 @@ function printObject(path, options, print) {
name === "this" ||
name === "rest") &&
number === 0
)
) ||
// Assignment printing logic (printAssignment) is responsible
// for adding a group if needed
(!shouldBreak &&
path.match(
(node) => node.type === "ObjectPattern",
(node) =>
node.type === "AssignmentExpression" ||
node.type === "VariableDeclarator"
))
) {
return content;
}
+6 -3
View File
@@ -93,14 +93,16 @@ function printPrettierIgnoredNode(node, options) {
const start = locStart(node);
const end = locEnd(node);
const printedComments = new Set();
for (const comment of comments) {
if (locStart(comment) >= start && locEnd(comment) <= end) {
comment.printed = true;
printedComments.add(comment);
}
}
return originalText.slice(start, end);
return { doc: originalText.slice(start, end), printedComments };
}
function callPluginPrintFunction(path, options, printPath, args) {
@@ -108,10 +110,11 @@ function callPluginPrintFunction(path, options, printPath, args) {
const { printer } = options;
let doc;
let printedComments;
// Escape hatch
if (printer.hasPrettierIgnore && printer.hasPrettierIgnore(path)) {
doc = printPrettierIgnoredNode(node, options);
({ doc, printedComments } = printPrettierIgnoredNode(node, options));
} else {
if (node) {
try {
@@ -139,7 +142,7 @@ function callPluginPrintFunction(path, options, printPath, args) {
) {
// printComments will call the plugin print function and check for
// comments to print
doc = printComments(path, doc, options);
doc = printComments(path, doc, options, printedComments);
}
return doc;
+56 -62
View File
@@ -15,7 +15,6 @@ const {
addLeadingComment,
addDanglingComment,
addTrailingComment,
isNonEmptyArray,
} = require("../common/util");
const childNodesCache = new WeakMap();
@@ -420,10 +419,10 @@ function breakTies(tiesToBreak, text, options) {
tiesToBreak.length = 0;
}
function printComment(commentPath, options) {
const comment = commentPath.getValue();
function printComment(path, options) {
const comment = path.getValue();
comment.printed = true;
return options.printer.printComment(commentPath, options);
return options.printer.printComment(path, options);
}
function findExpressionIndexForComment(quasis, comment, options) {
@@ -441,40 +440,45 @@ function findExpressionIndexForComment(quasis, comment, options) {
return 0;
}
function printLeadingComment(commentPath, options) {
const comment = commentPath.getValue();
const contents = printComment(commentPath, options);
/* istanbul ignore next */
if (!contents) {
return "";
}
const isBlock =
options.printer.isBlockComment && options.printer.isBlockComment(comment);
function printLeadingComment(path, options) {
const comment = path.getValue();
const parts = [printComment(path, options)];
const { printer, originalText, locStart, locEnd } = options;
const isBlock = printer.isBlockComment && printer.isBlockComment(comment);
// Leading block comments should see if they need to stay on the
// same line or not.
if (isBlock) {
const lineBreak = hasNewline(options.originalText, options.locEnd(comment))
? hasNewline(options.originalText, options.locStart(comment), {
const lineBreak = hasNewline(originalText, locEnd(comment))
? hasNewline(originalText, locStart(comment), {
backwards: true,
})
? hardline
: line
: " ";
return [contents, lineBreak];
parts.push(lineBreak);
} else {
parts.push(hardline);
}
return [contents, hardline];
const index = skipNewline(
originalText,
skipSpaces(originalText, locEnd(comment))
);
if (index !== false && hasNewline(originalText, index)) {
parts.push(hardline);
}
return parts;
}
function printTrailingComment(commentPath, options) {
const comment = commentPath.getValue();
const contents = printComment(commentPath, options);
/* istanbul ignore next */
if (!contents) {
return "";
}
function printTrailingComment(path, options) {
const comment = path.getValue();
const printed = printComment(path, options);
const { printer, originalText, locStart } = options;
const isBlock = printer.isBlockComment && printer.isBlockComment(comment);
@@ -497,17 +501,17 @@ function printTrailingComment(commentPath, options) {
locStart
);
return lineSuffix([hardline, isLineBeforeEmpty ? hardline : "", contents]);
return lineSuffix([hardline, isLineBeforeEmpty ? hardline : "", printed]);
}
let printed = [" ", contents];
let parts = [" ", printed];
// Trailing block comments never need a newline
if (!isBlock) {
printed = [lineSuffix(printed), breakParent];
parts = [lineSuffix(parts), breakParent];
}
return printed;
return parts;
}
function printDanglingComments(path, options, sameIndent, filter) {
@@ -518,15 +522,10 @@ function printDanglingComments(path, options, sameIndent, filter) {
return "";
}
path.each((commentPath) => {
const comment = commentPath.getValue();
if (
comment &&
!comment.leading &&
!comment.trailing &&
(!filter || filter(comment))
) {
parts.push(printComment(commentPath, options));
path.each(() => {
const comment = path.getValue();
if (!comment.leading && !comment.trailing && (!filter || filter(comment))) {
parts.push(printComment(path, options));
}
}, "comments");
@@ -540,41 +539,36 @@ function printDanglingComments(path, options, sameIndent, filter) {
return indent([hardline, join(hardline, parts)]);
}
function printCommentsSeparately(path, options) {
function printCommentsSeparately(path, options, ignored) {
const value = path.getValue();
const hasComments = isNonEmptyArray(value && value.comments);
const isCursorNode = Boolean(value && value === options.cursorNode);
if (!value) {
return {};
}
if (!hasComments) {
let comments = value.comments || [];
if (ignored) {
comments = comments.filter((comment) => !ignored.has(comment));
}
const isCursorNode = value === options.cursorNode;
if (comments.length === 0) {
const maybeCursor = isCursorNode ? cursor : "";
return { leading: maybeCursor, trailing: maybeCursor };
}
const leadingParts = [];
const trailingParts = [];
path.each(() => {
const comment = path.getValue();
if (ignored && ignored.has(comment)) {
return;
}
path.each((commentPath) => {
const comment = commentPath.getValue();
const { leading, trailing } = comment;
if (leading) {
const contents = printLeadingComment(commentPath, options);
/* istanbul ignore next */
if (!contents) {
return;
}
leadingParts.push(contents);
const text = options.originalText;
const index = skipNewline(
text,
skipSpaces(text, options.locEnd(comment))
);
if (index !== false && hasNewline(text, index)) {
leadingParts.push(hardline);
}
leadingParts.push(printLeadingComment(path, options));
} else if (trailing) {
trailingParts.push(printTrailingComment(commentPath, options));
trailingParts.push(printTrailingComment(path, options));
}
}, "comments");
@@ -586,8 +580,8 @@ function printCommentsSeparately(path, options) {
return { leading: leadingParts, trailing: trailingParts };
}
function printComments(path, doc, options) {
const { leading, trailing } = printCommentsSeparately(path, options);
function printComments(path, doc, options, ignored) {
const { leading, trailing } = printCommentsSeparately(path, options, ignored);
if (!leading && !trailing) {
return doc;
}
@@ -43,7 +43,7 @@ const unstableTests = new Map(
const [file, isUnstable = () => true] = Array.isArray(fixture)
? fixture
: [fixture];
return [path.join(__dirname, "../tests/", file), isUnstable];
return [path.join(__dirname, "../format/", file), isUnstable];
})
);
@@ -53,7 +53,7 @@ const espreeDisabledTests = new Set(
[
// These tests only work for `babel`
"comments-closure-typecast",
].map((directory) => path.join(__dirname, "../tests/js", directory))
].map((directory) => path.join(__dirname, "../format/js", directory))
);
const meriyahDisabledTests = espreeDisabledTests;
@@ -94,7 +94,7 @@ const shouldThrowOnFormat = (filename, options) => {
const isTestDirectory = (dirname, name) =>
(dirname + path.sep).startsWith(
path.join(__dirname, "../tests", name) + path.sep
path.join(__dirname, "../format", name) + path.sep
);
function runSpec(fixtures, parsers, options) {
File renamed without changes.
File renamed without changes.
@@ -2,6 +2,6 @@
Object.defineProperty(global, "run_spec", {
get() {
return require("./run_spec.js");
return require("./format-test");
},
});
Loaded 100 of 6814 files, more files were not shown because too many files have changed in this diff. Show more