Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add --cache CLI option #12800

Merged
merged 53 commits into from Jun 11, 2022
Merged

Add --cache CLI option #12800

merged 53 commits into from Jun 11, 2022

Conversation

sosukesuzuki
Copy link
Member

@sosukesuzuki sosukesuzuki commented May 6, 2022

Description

Fixes #5853

Two new CLI options have been added for a caching system similar to ESLint's one. Please see the documentation for details.

--cache

When you want to format only changed files, you can run Prettier with --cache flag. Enabling this flag can dramatically improve running time.

prettier --write --cache src

--cache-strategy

Strategy for the cache to use for detecting changed files. Can be either metadata or content. If no strategy is specified, metadata will be used.

prettier --write --cache --cache-strategy content src

Checklist

  • I’ve added tests to confirm my change works.
  • (If changing the API or CLI) I’ve documented the changes I’ve made (in the docs/ directory).
  • (If the change is user-facing) I’ve added my changes to changelog_unreleased/*/XXXX.md file following changelog_unreleased/TEMPLATE.md.
  • I’ve read the contributing guidelines.

Try the playground for this PR

@alexander-akait
Copy link
Member

Great job!

@fisker
Copy link
Sponsor Member

fisker commented May 7, 2022

This options should be implement on API side, not on CLI (like eslint/stylelint do), it is allow to use cache for node API users (plugins for IDE, custom CLI runner, etc)

#6577 (comment)

"cache-location": {
type: "path",
description: "Path to the cache file or directory",
default: ".prettiercache",
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Default to node_modules/.cache/prettier? https://github.com/avajs/find-cache-dir, #5910 also use this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in my opinion, node_modules/.cache is a really bad place to put the cache.
Because when using yarn, if any dependency is upgraded (not just prettier), then .cache is cleared.
find-cache-dir does allow specifying a CACHE_DIR so I can use that to get around it, but if you use alot of packages and upgrade daily and use yarn, it make the cache option useless.

@sosukesuzuki
Copy link
Member Author

This options should be implement on API side, not on CLI (like eslint/stylelint do), it is allow to use cache for node API users (plugins for IDE, custom CLI runner, etc)

@fisker /cc @alexander-akait The current Prettier JavaScript API does not take a file path as an argument. Therefore, I don't think it is possible to implement a file-based cache in the API.

* Find default cache file (`./node_modules/.cache/prettier/.prettiercache`) using https://github.com/avajs/find-cache-dir
*/
function findDefaultCacheFile() {
const cacheDirThunk = findCacheDir({ name: "prettier", thunk: true });
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

src/cli/utils.js Outdated
* @returns {string}
*/
function createHash(source) {
return crypto.createHash("md5").update(source).digest("hex");
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sdbm looks good f1b2c74

Comment on lines +37 to +39
function getMetadataFromFileDescriptor(fileDescriptor) {
return fileDescriptor.meta;
}
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we use fileDescriptor.meta directly? This just making code less readable.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just for type checking...

default: false,
description: "Only format changed files",
type: "boolean",
},
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we enable it by default?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At least we cannot enable it by default until v3

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am thinking about it too, but most of tools disable cache by default - not to waste extra disk space without allowing, also some CI can work in read only access or with write access only for specified dirs, so if we enable it by default in v2, we can break CI

"cache-location": {
description:
"Path to the cache file or directory\nDefaults to node_modules/.cache/prettier/.prettiercache",
type: "path",
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added cache-location options because ESLint has same option. But, I think we can remove this.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it makes sense to leave cache-location. It's a good idea to leave users the option to control where this cache is stored. It's useful in CI at least. Usually, people cache their node_modules by yarn.lock and it could lead to undesired caching of prettier.

Also removing node_modules for reinstall is still a widely spreaded approach and it will wipe all prettier cache

Copy link
Sponsor Member

@fisker fisker May 21, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why don't we add it if someone request in future?

cspell.json Outdated Show resolved Hide resolved
return optionsHashCache.get(options);
}
const hash = createHash(
`${prettierVersion}_${nodeVersion}_${stringify(options)}`
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We allow functions in options, not sure how should we handle this.

plugins: [require('prettier-plugin-foo')]

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have no idea to serialize an option that includes functions for plugins. What do you think about to mention about behavior for plugins and caching.

If you are using plugins in your Prettier config file, caching may not work well. If caching does not work well after updating the plugin, try running Prettier without the `--cache` option to clear the cache.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

* @param {any} options
*/
existsAvailableFormatResultsCache(filePath, options) {
const fileDescriptor = this.fileEntryCache.getFileDescriptor(filePath);
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Feel not safe without file content, but that will be slow.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For minimum speed and security, why not check the length of the file content rather than the content of the file?

Copy link

@7rulnik 7rulnik May 20, 2022

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In this case it's not usable in CI. ESLint, for example, has --cache-strategy which allows to use metadata or content for detecting changed files

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, I've added --cache-strategy option!

@@ -362,17 +368,28 @@ async function formatFiles(context) {

const start = Date.now();

const existsCache = formatResultsCache?.existsAvailableFormatResultsCache(
filename,
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we use absolute path here?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why? I think current implementation is enough.

@kachkaev
Copy link
Member

kachkaev commented May 10, 2022

How realistic would it be to cache markdown code blocks on their own (as if there were separate files)?

I am maintaining prettier-plugin-elm which calls elm-format fore every code block. This is pretty expensive, so I have to cache Elm code blocks within the plugin. Offloading caching to Prettier core would be amazing because my plugin will be free from side effects!

const { createHash } = require("./utils.js");

const optionsHashCache = new WeakMap();
const nodeVersion = process && process.version;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why we need it? Something in our code depend on Node.js version?

@alexander-akait
Copy link
Member

@sosukesuzuki

@fisker /cc @alexander-akait The current Prettier JavaScript API does not take a file path as an argument. Therefore, I don't think it is possible to implement a file-based cache in the API.

Good question. But we have a glob, can we use it?

@fisker
Copy link
Sponsor Member

fisker commented May 21, 2022

I think @sosukesuzuki is right. it's impossible to add this in API, unless we add something like formatFile that ignores the input.

@sosukesuzuki sosukesuzuki changed the title Add new --cache and --cache-location Add --cache CLI optio May 27, 2022
@sosukesuzuki sosukesuzuki changed the title Add --cache CLI optio Add --cache CLI option May 27, 2022
@sosukesuzuki sosukesuzuki added this to the 2.7 milestone May 27, 2022
src/cli/format.js Outdated Show resolved Hide resolved
src/cli/format.js Outdated Show resolved Hide resolved
src/cli/constant.js Outdated Show resolved Hide resolved
src/cli/find-cache-file.js Outdated Show resolved Hide resolved
src/cli/format-results-cache.js Outdated Show resolved Hide resolved
tests/integration/__tests__/cache.js Show resolved Hide resolved
@@ -0,0 +1,19 @@
#### Add `--cache` CLI option (#12800 by @sosukesuzuki)
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missed cache-strategy in title.


##### `--cache`

When you want to format only changed files, you can run Prettier with `--cache` flag. Enabling this flag can dramatically improve running time.
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sounds like CLI will skip unchanged files, but it's not.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is wrong?

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Even if file is unchanged, but last run was not using --cache we'll format it.

Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or only config changed, but file didn't change.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got it. However, I have no idea to better sentence now... I'll figure it out later.

@@ -0,0 +1,19 @@
#### Add `--cache` CLI option (#12800 by @sosukesuzuki)

Two new CLI options have been added for a caching system similar to [ESLint's one](https://eslint.org/docs/user-guide/command-line-interface#caching). Please see the [documentation](https://prettier.io/docs/en/cli.html#--cache) for details.
Copy link
Sponsor Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The link to docs seems unnecessary, you already explained here.

src/cli/utils.js Outdated Show resolved Hide resolved
@sosukesuzuki
Copy link
Member Author

How realistic would it be to cache markdown code blocks on their own (as if there were separate files)?

@kachkaev Sorry I forgot to reply to you. Since the cache feature implemented here is a per-file cache, it is not possible to use this feature for caching Markdown code blocks.

If caching Markdown code blocks is beneficial enough for the Prettier core, it could be implemented in the future( But don't expect...)

bpaquet added a commit to bpaquet/prettier that referenced this pull request Jun 3, 2022
@sosukesuzuki
Copy link
Member Author

@fisker @alexander-akait @lydell I will merge for now, but if you have any concerns, please comment again before the release.

output = result.formatted;
} catch (error) {
handleError(context, filename, error, printedFilename);
continue;
}

formatResultsCache?.setFormatResultsCache(filename, options);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we’re running without --write, this incorrectly caches the unformatted file rather than the formatted file, which makes the --cache option unusable. Please merge #13016.

medikoo pushed a commit to medikoo/prettier-elastic that referenced this pull request Jan 4, 2024
* Install `file-entry-cache` and types

* Implement `FormatResultsCache`

* Add `--cache` and `--cache-location` option

* Implement `--cache` and `--cache-location`

* Add tests for `--cache`

* Add tests for `--cache-location`

* Avoid spellcheck error

* Update snapshots for new options

* Add `groupBy` for website

* Use `fs.promises` instead of `fs/promises`

* Use `rimraf` instead of `fs.rm`

* Fix properties order of `options`

* Install `find-cache-dir` and types

* Change default cache file location to `node_modules/.cache/prettier`

* Fix tests following to change default cache file location

* Update snapshots for changing default cache file location

* Set `os.tmpdir()` as a fallback for default cache dir

* Add docs for `--cache` and `--cache-location`

* Use `sdbm` instead of `node:crypto`

* Add changelog

* Fix `getHashOfOptions`

* Remove `cache-location`

* `prettiercache` -> `prettier-cache`

* Remove cache file when run Prettier without `--cache` option

* Implement `--cache-strategy`

* Add tests for invalid cache strategy

* Fix lint problems

* Tweaks tests

* Add tests for timestamp

* Add tests for `--cache-strategy`

* Add docs for cache-strategy

* Address review

* Fix `findCacheFile`

* Use Set.prototype.has

* Throw error with --stdin-filepath

* Update snapshots

* Fix error for cache and stdin

* Use string constructor instead of toString

* Update changelog

* Use flag validation

* Fix `cache-strategy` definition

* Update docs

* Mark highlihgt

* Throw error for `--cache-strategy` without `--cache`

* Update docs

* Fix typo

* Updates snapshots

* Update docs/cli.md

Co-authored-by: Simon Lydell <simon.lydell@gmail.com>

* Fix error message

* Remove `:::` syntax

* Defaults `content`

* Update docs

* Fix by Prettier

Co-authored-by: Simon Lydell <simon.lydell@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add cache option to avoid processing unchanged files
9 participants