diff --git a/.github/renovate.json5 b/.github/renovate.json5
index d3ef9eed0..f3dd88ef6 100644
--- a/.github/renovate.json5
+++ b/.github/renovate.json5
@@ -1,17 +1,32 @@
{
$schema: "https://docs.renovatebot.com/renovate-schema.json",
- labels: ["scope:dependency"],
+ labels: ["dependency"],
baseBranches: ["next"],
- // schedule: ["at 01:00 on sunday"],
+ schedule: ["after 01:00 on sunday"],
+ postUpdateOptions: ["yarnDedupeHighest"],
packageRules: [
{
+ matchPackageNames: ["react", "react-dom"],
+ groupName: "react",
+ },
+ {
+ matchPackagePrefixes: ["@babel/"],
+ excludePackageNames: ["@babel/parser"],
+ groupName: "babel",
+ },
+ {
+ branchPrefix: "renovate/website/",
+ matchPaths: ["website/package.json"],
+ labels: ["dependency:website"],
+ },
+ {
+ branchPrefix: "renovate/tools/",
matchPaths: [
- "package.json",
- "website/package.json",
"scripts/release/package.json",
"scripts/tools/bundle-test/package.json",
"scripts/tools/eslint-plugin-prettier-internal-rules/package.json",
],
+ labels: ["dependency:tools"],
},
],
}
diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml
index e542a31c9..9203dd5c8 100644
--- a/.github/workflows/lint.yml
+++ b/.github/workflows/lint.yml
@@ -45,6 +45,9 @@ jobs:
- name: Lint workflow files
run: yarn lint:actionlint
+ - name: Validate renovate config
+ run: npx --package renovate -c renovate-config-validator
+
- name: Run yarn (/)
run: |
yarn
diff --git a/.gitpod.yml b/.gitpod.yml
new file mode 100644
index 000000000..69873400d
--- /dev/null
+++ b/.gitpod.yml
@@ -0,0 +1,25 @@
+# https://gitpod.io/#https://github.com/prettier/prettier
+
+tasks:
+ - init: |
+ yarn
+ yarn build
+
+github:
+ # https://www.gitpod.io/docs/prebuilds/#configure-prebuilds
+ prebuilds:
+ master: true # enable for the default branch (even if it's not named "master")
+ branches: true
+ pullRequests: true
+ pullRequestsFromForks: true
+ addCheck: true
+ addComment: false
+ addBadge: true
+ addLabel: false
+
+vscode:
+ extensions:
+ - esbenp.prettier-vscode
+ - dbaeumer.vscode-eslint
+ - editorconfig.editorconfig
+ - streetsidesoftware.code-spell-checker
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 5a4ac34b8..782331562 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -78,24 +78,20 @@ If you want to know more about Prettier's GitHub labels, see the [Issue Labels](
If you're contributing a performance improvement, the following Prettier CLI options can help:
-- `--debug-repeat N` uses a naïve loop to repeat the formatting `N` times and measures the average run duration. It can be useful to highlight hot functions in the profiler. The measurements are printed at the debug log level, use `--loglevel debug` to see them.
-- `--debug-benchmark` uses [`benchmark`](https://npm.im/benchmark) module to produce statistically significant duration measurements. The measurements are printed at the debug log level, use `--loglevel debug` to see them.
+- `--debug-repeat N` uses a naïve loop to repeat the formatting `N` times and measures the average run duration. It can be useful to highlight hot functions in the profiler. This can also set by environment variable `PRETTIER_PERF_REPEAT`.
+- `--debug-benchmark` uses [`benchmark`](https://npm.im/benchmark) module to produce statistically significant duration measurements.
For convenience, the following commands for profiling are available via `package.json` `scripts`.
-_Unfortunately, [`yarn` simply appends passed arguments to commands, cannot reference them by name](https://github.com/yarnpkg/yarn/issues/5207), so we have to use inline environment variables to pass them._
-
-- `PERF_FILE= PERF_REPEAT=[number-of-repetitions:1000] yarn perf:repeat` starts the naïve loop. See the CLI output for when the measurements finish, and stop profiling at that moment.
-- `PERF_FILE= PERF_REPEAT=[number-of-repetitions:1000] yarn perf:repeat-inspect` starts the naïve loop with `node --inspect-brk` flag that pauses execution and waits for Chromium/Chrome/Node Inspector to attach. Open [`chrome://inspect`](chrome://inspect), select the process to inspect, and activate the CPU Profiler, this will unpause execution. See the CLI output for when the measurements finish, and stop the CPU Profiler at that moment to avoid collecting more data than needed.
-- `PERF_FILE= yarn perf:benchmark` starts the `benchmark`-powered measurements. See the CLI output for when the measurements finish.
+- `PRETTIER_PERF_REPEAT=1000 yarn perf ` starts the naïve loop. See the CLI output for when the measurements finish, and stop profiling at that moment.
+- `PRETTIER_PERF_REPEAT=1000 yarn perf:inspect ` starts the naïve loop with `node --inspect-brk` flag that pauses execution and waits for Chromium/Chrome/Node Inspector to attach. Open [`chrome://inspect`](chrome://inspect), select the process to inspect, and activate the CPU Profiler, this will unpause execution. See the CLI output for when the measurements finish, and stop the CPU Profiler at that moment to avoid collecting more data than needed.
+- `yarn perf:benchmark ` starts the `benchmark`-powered measurements. See the CLI output for when the measurements finish.
In the above commands:
- `yarn && yarn build` ensures the compiler-optimized version of Prettier is built prior to launching it. Prettier's own environment checks are defaulted to production and removed during the build. The build output is cached, so a rebuild will happen only if the source code changes.
- `NODE_ENV=production` ensures Prettier and its dependencies run in production mode.
- `node --inspect-brk` pauses the script execution until Inspector is connected to the Node process.
-- `--loglevel debug` ensures the `--debug-repeat` or `--debug-benchmark` measurements are printed to `stderr`.
-- `> /dev/null` ensures the formatted output is discarded.
In addition to the options above, you can use [`node --prof` and `node --prof-process`](https://nodejs.org/en/docs/guides/simple-profiling/), as well as `node --trace-opt --trace-deopt`, to get more advanced performance insights.
diff --git a/README.md b/README.md
index b439216dc..943299e12 100644
--- a/README.md
+++ b/README.md
@@ -54,8 +54,6 @@
-
-
diff --git a/changelog_unreleased/cli/12682.md b/changelog_unreleased/cli/12682.md
index 135d87bb8..c78946004 100644
--- a/changelog_unreleased/cli/12682.md
+++ b/changelog_unreleased/cli/12682.md
@@ -1,3 +1,6 @@
-#### Skip print code or write file when performance flags found (#12682 by @fisker)
+#### Simplify performance test (#12682, #12698 by @fisker)
-When `--debug-benchmark` or `--debug-repeat` is used, the CLI will skip print the formatted code to the screen or write files.
+When `--debug-benchmark` or `--debug-repeat` is passed:
+
+1. The CLI skips print code to the screen or write file
+1. Set log level to `debug` automatically
diff --git a/changelog_unreleased/html/12882.md b/changelog_unreleased/html/12882.md
new file mode 100644
index 000000000..3e94364b6
--- /dev/null
+++ b/changelog_unreleased/html/12882.md
@@ -0,0 +1,32 @@
+#### Support Speculation Rules API formatting in HTML (#12882 by @sosukesuzuki)
+
+Please read https://web.dev/speculative-prerendering/ for about Speculation Rules API.
+
+
+```html
+
+
+
+
+
+
+
+
+
+```
diff --git a/changelog_unreleased/javascript/12779.md b/changelog_unreleased/javascript/12779.md
new file mode 100644
index 000000000..c59e999fb
--- /dev/null
+++ b/changelog_unreleased/javascript/12779.md
@@ -0,0 +1,77 @@
+#### Make more callee patterns recognized as "test call callee". (#12779 by @HosokawaR)
+
+Supports test calls like [Playwright `test.describe`](https://playwright.dev/docs/api/class-test#test-describe).
+
+
+```jsx
+// Input
+test.step("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe.only("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe.parallel("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe.parallel.only("does something really long and complicated so I have to write a very long name for the testThis is a very", () => {});
+
+test.describe.serial("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe.serial.only("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+// Prettier stable
+test.step(
+ "does something really long and complicated so I have to write a very long name for the test",
+ () => {}
+);
+
+test.describe(
+ "does something really long and complicated so I have to write a very long name for the test",
+ () => {}
+);
+
+test.describe.only(
+ "does something really long and complicated so I have to write a very long name for the test",
+ () => {}
+);
+
+test.describe.parallel(
+ "does something really long and complicated so I have to write a very long name for the test",
+ () => {}
+);
+
+test.describe.parallel.only(
+ "does something really long and complicated so I have to write a very long name for the testThis is a very",
+ () => {}
+);
+
+test.describe.serial(
+ "does something really long and complicated so I have to write a very long name for the test",
+ () => {}
+);
+
+test.describe.serial.only(
+ "does something really long and complicated so I have to write a very long name for the test",
+ () => {}
+);
+
+// Prettier main
+test.step("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe
+ .only("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe
+ .parallel("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe.parallel
+ .only("does something really long and complicated so I have to write a very long name for the testThis is a very", () => {});
+
+test.describe
+ .serial("does something really long and complicated so I have to write a very long name for the test", () => {});
+
+test.describe.serial
+ .only("does something really long and complicated so I have to write a very long name for the test", () => {});
+```
diff --git a/changelog_unreleased/javascript/12860.md b/changelog_unreleased/javascript/12860.md
new file mode 100644
index 000000000..22497b9e0
--- /dev/null
+++ b/changelog_unreleased/javascript/12860.md
@@ -0,0 +1,49 @@
+#### Fix comment formats in (#12860 by @HosokawaR)
+
+This change fixes comments format in `exports` to algin with the comment format in `import`
+
+Although this change does not affect the comments format in `import`, follows change log contains examples of comments in `import` for reference.
+
+
+```jsx
+// Input
+export {
+ foo,
+
+ bar as // comment
+ baz,
+}
+
+import {
+ foo,
+
+ bar as // comment
+ baz,
+} from 'foo'
+
+// Prettier stable
+export {
+ foo,
+ bar as baz, // comment
+};
+
+import {
+ foo,
+ // comment
+ bar as baz,
+} from "foo";
+
+// Prettier main
+export {
+ foo,
+ // comment
+ bar as baz,
+};
+
+import {
+ foo,
+ // comment
+ bar as baz,
+} from "foo";
+
+```
diff --git a/changelog_unreleased/typescript/12896.md b/changelog_unreleased/typescript/12896.md
new file mode 100644
index 000000000..d1b07bd0d
--- /dev/null
+++ b/changelog_unreleased/typescript/12896.md
@@ -0,0 +1,37 @@
+#### [HIGHLIGHT]Support TypeScript 4.7 (#12896, #12897, #12898, #12900, #12921, #12924 by @sosukesuzuki)
+
+Support [TypeScript 4.7](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7) new features!
+
+##### [Instantiation Expressions](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#instantiation-expressions)
+
+```ts
+interface Box {
+ value: T;
+}
+function makeBox(value: T) {
+ return { value };
+}
+const makeHammerBox = makeBox;
+const makeWrenchBox = makeBox;
+```
+
+##### [Optional Variance](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#optional-variance-annotations-for-type-parameters)
+
+```ts
+interface Animal {
+ animalStuff: any;
+}
+interface Dog extends Animal {
+ dogStuff: any;
+}
+type Getter = () => T;
+type Setter = (value: T) => void;
+```
+
+##### [`extends` constraints for `infer`](https://devblogs.microsoft.com/typescript/announcing-typescript-4-7/#extends-constraints-on-infer-type-variables)
+
+```ts
+type FirstString = T extends [infer extends string, ...unknown[]]
+ ? S
+ : never;
+```
diff --git a/changelog_unreleased/typescript/12930.md b/changelog_unreleased/typescript/12930.md
new file mode 100644
index 000000000..9c6fde5af
--- /dev/null
+++ b/changelog_unreleased/typescript/12930.md
@@ -0,0 +1,19 @@
+#### fix formatting for typescript Enum with computed members (#12930 by @HosokawaR)
+
+
+```tsx
+// Input
+enum A {
+ [i++],
+}
+
+// Prettier stable
+enum A {
+ i++,
+}
+
+// Prettier main
+enum A {
+ [i++],
+}
+```
diff --git a/changelog_unreleased/vue/12707.md b/changelog_unreleased/vue/12707.md
new file mode 100644
index 000000000..1c90157bb
--- /dev/null
+++ b/changelog_unreleased/vue/12707.md
@@ -0,0 +1,3 @@
+#### Infer Stylus parser for Vue SFC style block (#12707 by @lsdsjy)
+
+`
+
+
=====================================output=====================================
+
+
================================================================================
`;
@@ -141,16 +155,31 @@ printWidth: 80
| printWidth
=====================================input======================================
+
+
=====================================output=====================================
+
+
================================================================================
`;
diff --git a/tests/format/vue/with-plugins/jsfmt.spec.js b/tests/format/vue/with-plugins/jsfmt.spec.js
index 638477853..6ccb205a7 100644
--- a/tests/format/vue/with-plugins/jsfmt.spec.js
+++ b/tests/format/vue/with-plugins/jsfmt.spec.js
@@ -4,6 +4,7 @@ const { require } = createEsmUtils(import.meta);
const plugins = [
require("../../../config/prettier-plugins/prettier-plugin-uppercase-rocks/index.cjs"),
+ require("../../../config/prettier-plugins/prettier-plugin-dummy-stylus/index.cjs"),
];
run_spec(import.meta, ["vue"], { plugins });
diff --git a/tests/format/vue/with-plugins/style-lang.vue b/tests/format/vue/with-plugins/style-lang.vue
index 0c02068df..4996e4d30 100644
--- a/tests/format/vue/with-plugins/style-lang.vue
+++ b/tests/format/vue/with-plugins/style-lang.vue
@@ -1,4 +1,11 @@
+
+
diff --git a/website/core/Footer.js b/website/core/Footer.js
index 32a4f5abd..97a5846c8 100644
--- a/website/core/Footer.js
+++ b/website/core/Footer.js
@@ -59,7 +59,6 @@ class Footer extends React.Component {
>
Stack Overflow
- Chat on Gitter
@PrettierCode on Twitter
diff --git a/yarn.lock b/yarn.lock
index 7fb55690b..c708d9eff 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -217,7 +217,16 @@ __metadata:
languageName: node
linkType: hard
-"@babel/parser@npm:7.17.10, @babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.17.10":
+"@babel/parser@npm:7.18.0":
+ version: 7.18.0
+ resolution: "@babel/parser@npm:7.18.0"
+ bin:
+ parser: ./bin/babel-parser.js
+ checksum: 253b5828bf4a0b443301baedc5993d6f7f35aa0d81cf8f2f2f53940904b7067eab7bd2380aee4b3be1d8efd5ae1008eb0fad19bde28f5fbc213c0fdf9a414466
+ languageName: node
+ linkType: hard
+
+"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.14.7, @babel/parser@npm:^7.16.7, @babel/parser@npm:^7.17.10":
version: 7.17.10
resolution: "@babel/parser@npm:7.17.10"
bin:
@@ -1545,13 +1554,6 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/types@npm:5.23.0":
- version: 5.23.0
- resolution: "@typescript-eslint/types@npm:5.23.0"
- checksum: 96ae3e80cfae7b34f2846db692c31fb1804bf9651bce1d29f2eb8ae4c763d22f3283adc02dedeebd7cf70e4d8be54ec7f6ca593e03cdca26c791207e7556c2c1
- languageName: node
- linkType: hard
-
"@typescript-eslint/types@npm:5.25.0":
version: 5.25.0
resolution: "@typescript-eslint/types@npm:5.25.0"
@@ -1559,21 +1561,10 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/typescript-estree@npm:5.23.0":
- version: 5.23.0
- resolution: "@typescript-eslint/typescript-estree@npm:5.23.0"
- dependencies:
- "@typescript-eslint/types": 5.23.0
- "@typescript-eslint/visitor-keys": 5.23.0
- debug: ^4.3.2
- globby: ^11.0.4
- is-glob: ^4.0.3
- semver: ^7.3.5
- tsutils: ^3.21.0
- peerDependenciesMeta:
- typescript:
- optional: true
- checksum: 8d85bb1cd777e93cc7322ae8fea25f9b924def02494cdb8395c1d5d17b5fd3ac9bc969418a1d20a5dc28c2cdd85da20e13527e28b595c06ff6f84cd22a78d73f
+"@typescript-eslint/types@npm:5.26.0":
+ version: 5.26.0
+ resolution: "@typescript-eslint/types@npm:5.26.0"
+ checksum: 98798616d832da8e5ef61f050e4e72ed921a162cb4ce2b2dfe0a317c66998157e832f449aeab21a1fdfd806e7134091bc1a9446b1089f4687786b646ad8738e7
languageName: node
linkType: hard
@@ -1595,6 +1586,24 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/typescript-estree@npm:5.26.0":
+ version: 5.26.0
+ resolution: "@typescript-eslint/typescript-estree@npm:5.26.0"
+ dependencies:
+ "@typescript-eslint/types": 5.26.0
+ "@typescript-eslint/visitor-keys": 5.26.0
+ debug: ^4.3.4
+ globby: ^11.1.0
+ is-glob: ^4.0.3
+ semver: ^7.3.7
+ tsutils: ^3.21.0
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+ checksum: 2cf147629474952725593da64827a7e4e39f79614019529d0d6e5b89236c55f3607c6b4a24f160dbc5978f4cfd60f96fcb573a770c2877f8e29d7552b40e2135
+ languageName: node
+ linkType: hard
+
"@typescript-eslint/utils@npm:5.25.0, @typescript-eslint/utils@npm:^5.10.0":
version: 5.25.0
resolution: "@typescript-eslint/utils@npm:5.25.0"
@@ -1611,16 +1620,6 @@ __metadata:
languageName: node
linkType: hard
-"@typescript-eslint/visitor-keys@npm:5.23.0":
- version: 5.23.0
- resolution: "@typescript-eslint/visitor-keys@npm:5.23.0"
- dependencies:
- "@typescript-eslint/types": 5.23.0
- eslint-visitor-keys: ^3.0.0
- checksum: 322e10d52a985e8a90d3612bb9d09a87dc64fc4cb1248484f1a9a7a98f65d3ef65a465ce868773a4939e35fa3b726ad609dac5a168efd7eaca4b06df33e965e3
- languageName: node
- linkType: hard
-
"@typescript-eslint/visitor-keys@npm:5.25.0":
version: 5.25.0
resolution: "@typescript-eslint/visitor-keys@npm:5.25.0"
@@ -1631,6 +1630,16 @@ __metadata:
languageName: node
linkType: hard
+"@typescript-eslint/visitor-keys@npm:5.26.0":
+ version: 5.26.0
+ resolution: "@typescript-eslint/visitor-keys@npm:5.26.0"
+ dependencies:
+ "@typescript-eslint/types": 5.26.0
+ eslint-visitor-keys: ^3.3.0
+ checksum: 4a5085d19e677f3b44ca4455bba85fd1a3be4d7d2e96bb22738a7497f6f4d16bfdee51059454a78b1e108d8e1593b1a31be40764a66448945118277cff5eff02
+ languageName: node
+ linkType: hard
+
"abbrev@npm:1":
version: 1.1.1
resolution: "abbrev@npm:1.1.1"
@@ -3427,7 +3436,7 @@ __metadata:
languageName: node
linkType: hard
-"eslint-visitor-keys@npm:^3.0.0, eslint-visitor-keys@npm:^3.3.0":
+"eslint-visitor-keys@npm:^3.3.0":
version: 3.3.0
resolution: "eslint-visitor-keys@npm:3.3.0"
checksum: d59e68a7c5a6d0146526b0eec16ce87fbf97fe46b8281e0d41384224375c4e52f5ffb9e16d48f4ea50785cde93f766b0c898e31ab89978d88b0e1720fbfb7808
@@ -4037,7 +4046,7 @@ __metadata:
languageName: node
linkType: hard
-"globby@npm:^11.0.1, globby@npm:^11.0.4, globby@npm:^11.1.0":
+"globby@npm:^11.0.1, globby@npm:^11.1.0":
version: 11.1.0
resolution: "globby@npm:11.1.0"
dependencies:
@@ -6658,7 +6667,7 @@ __metadata:
dependencies:
"@angular/compiler": 12.2.16
"@babel/code-frame": 7.16.7
- "@babel/parser": 7.17.10
+ "@babel/parser": 7.18.0
"@babel/types": 7.18.0
"@esbuild-plugins/node-modules-polyfill": 0.1.4
"@glimmer/reference": 0.84.2
@@ -6667,7 +6676,7 @@ __metadata:
"@types/estree": 0.0.51
"@types/jest": 27.5.1
"@typescript-eslint/eslint-plugin": 5.25.0
- "@typescript-eslint/typescript-estree": 5.23.0
+ "@typescript-eslint/typescript-estree": 5.26.0
acorn: 8.7.1
acorn-jsx: 5.3.2
angular-estree-parser: 2.5.0
@@ -6758,7 +6767,7 @@ __metadata:
string-width: 5.0.1
strip-ansi: 7.0.1
tempy: 2.0.0
- typescript: 4.6.4
+ typescript: 4.7.2
unicode-regex: 3.0.0
unified: 9.2.2
vnopts: 1.0.2
@@ -8019,23 +8028,23 @@ __metadata:
languageName: node
linkType: hard
-"typescript@npm:4.6.4":
- version: 4.6.4
- resolution: "typescript@npm:4.6.4"
+"typescript@npm:4.7.2":
+ version: 4.7.2
+ resolution: "typescript@npm:4.7.2"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
- checksum: e7bfcc39cd4571a63a54e5ea21f16b8445268b9900bf55aee0e02ad981be576acc140eba24f1af5e3c1457767c96cea6d12861768fb386cf3ffb34013718631a
+ checksum: 5163585e6b56410f77d5483b698d9489bbee8902c99029eb70cf6d21525a186530ce19a00951af84eefd4a131cc51d0959f5118e25e70ab61f45ac4057dbd1ef
languageName: node
linkType: hard
-"typescript@patch:typescript@4.6.4#~builtin":
- version: 4.6.4
- resolution: "typescript@patch:typescript@npm%3A4.6.4#~builtin::version=4.6.4&hash=7ad353"
+"typescript@patch:typescript@4.7.2#~builtin":
+ version: 4.7.2
+ resolution: "typescript@patch:typescript@npm%3A4.7.2#~builtin::version=4.7.2&hash=7ad353"
bin:
tsc: bin/tsc
tsserver: bin/tsserver
- checksum: 1cb434fbc637d347be90e3a0c6cd05e33c38f941713c8786d3031faf1842c2c148ba91d2fac01e7276b0ae3249b8633f1660e32686cc7a8c6a8fd5361dc52c66
+ checksum: 7e2b9a9f4a70fb7616f1b0d986977f8e34a74f046202fa7f24fdee79589598277810fa216b3776c20c0683a9235872c73be34fdb93f67f98c1efaca40999422f
languageName: node
linkType: hard