I think it's easier to review this PR commit by commit, so I'll explain it this way: ## Commits 1. [parallelize eslint script (still showing logs results in serial)](c9ac49c12d): Previously, `--concurrency 1` was added to the script to make the logs more readable. However, turborepo has an option specifically for these use cases: `--log-order=grouped` runs the tasks in parallel but outputs them serially. As a result, the lint script is now significantly faster. 2. [run pnpm lint:fix](9c128c276a) The auto-fix was run, which resolved some eslint errors that were slipped in due to the use of `no-verify`. Most of these were `perfectionist` fixes (property ordering) and the removal of unnecessary assertions. Starting with this PR, this won't happen again in the future, as we'll be verifying the linter in every PR across the entire codebase (see commit 7). 3. [fix eslint non-autofixable errors](700f412a33) All manual errors have been resolved except for the configuration errors addressed in commit 5. Most were React compiler violations, which have been disabled and commented out "TODO" for now. There's also an unused `use no memo` and a couple of `require` errors. 4. [move react-compiler linter to eslint-config package](4f7cb4d63a) To simplify the eslint configuration. My concern was that there would be a performance regression when used in non-react related packages, but none was experienced. This is probably because it only runs on .tsx files. 5. [remove redundant eslint config files and fix allowDefaultProject](a94347995a) The main feature introduced by `typescript-eslint` v8 was `projectService`, which automatically searches each file for the closest `tsconfig`, greatly simplifying configuration in monorepos ([source](https://typescript-eslint.io/blog/announcing-typescript-eslint-v8#project-service)). Once I moved `projectService` to `packages/eslint-config`, all the other configuration files could be easily removed. I confirmed that pnpm lint still works on individual packages. The other important change was that the pending eslint errors from commits 2 and 3 were resolved. That is, some files were giving the error: "[File] was not found by the project service. Consider either including it in the tsconfig.json or including it in allowDefaultProject." Below I copy the explanatory comment I left in the code: ```ts // This is necessary because `tsconfig.base.json` defines `"rootDir": "${configDir}/src"`, // And the following files aren't in src because they aren't transpiled. // This is typescript-eslint's way of adding files that aren't included in tsconfig. // See: https://typescript-eslint.io/troubleshooting/typed-linting/#i-get-errors-telling-me--was-not-found-by-the-project-service-consider-either-including-it-in-the-tsconfigjson-or-including-it-in-allowdefaultproject // The best practice is to have a tsconfig.json that covers ALL files and is used for // typechecking (with noEmit), and a `tsconfig.build.json` that is used for the build // (or alternatively, swc, tsup or tsdown). That's what we should ideally do, in which case // this hardcoded list wouldn't be necessary. Note that these files don't currently go // through ts, only through eslint. ``` 6. [Differentiate errors from warnings in VScode ESLint Rules](5914d2f48d) There's no reason to do that. If an eslint rule isn't an error, it should be disabled or converted to a warning. 7. [Disable skip lint, and lint over the entire repo now that it's faster](e4b28f1360) The GitHub action linted only the files that had changed in the PR. While this seems like a good idea, once exceptions were introduced with [skip lint], they opened the door to propagating more and more errors. Often, the linter was skipped, not because someone introduced new errors, but because they were trying to avoid those that had already crept in, sometimes accidentally introducing new ones. On the other hand, `pnpm lint` now runs in parallel (commit 1), so it's not that slow. Additionally, it runs in parallel with other GitHub actions like e2e tests, which take much longer, so it can't represent a bottleneck in CI. 8. [fix lint in next package](4506595f91) Small fix missing from commit 5 9. [Merge remote-tracking branch 'origin/main' into fix-eslint](563d4909c1) 10. [add again eslint.config.js in payload package](78f6ffcae7) The comment in the code explains it. Basically, after the merge from main, the payload package runs out of memory when linting, probably because it grew in recent PRs. That package will sooner or later collapse for our tooling, so we may have to split it. It's already too big. ## Future Actions - Resolve React compiler violations, as mentioned in commit 3. - Decouple the `tsconfig` used for typechecking and build across the entire monorepo (as explained in point 5) to ensure ts coverage even for files that aren't transpiled (such as scripts). - Remove the few remaining `eslint.config.js`. I had to leave the `richtext-lexical` and `next` ones for now. They could be moved to the root config and scoped to their packages, as we do for example with `templates/vercel-postgres/**`. However, I couldn't get it to work, I don't know why. - Make eslint in the test folder usable. Not only are we not linting `test` in CI, but now the `pnpm eslint .` command is so large that my computer freezes. If each suite were its own package, this would be solved, and dynamic codegen + git hooks to modify tsconfig.base.json wouldn't be necessary ([related](https://github.com/payloadcms/payload/pull/11984)).
Payload Cloud Storage Plugin
This repository contains the officially supported Payload Cloud Storage plugin. It extends Payload to allow you to store all uploaded media in third-party permanent storage.
NOTE: If you are using Payload 3.0 and one of the following storage services, you should use one of following packages instead of this one:
| Service | Package |
|---|---|
| Vercel Blob | @payloadcms/storage-vercel-blob |
| AWS S3 | @payloadcms/storage-s3 |
| Azure | @payloadcms/storage-azure |
| Google Cloud Storage | @payloadcms/storage-gcs |
This package is now best used for implementing custom storage solutions or third-party storage services that do not have @payloadcms/storage-* packages.
Installation
pnpm add @payloadcms/plugin-cloud-storage
Usage
import { buildConfig } from 'payload'
import { cloudStoragePlugin } from '@payloadcms/plugin-cloud-storage'
export default buildConfig({
plugins: [
cloudStoragePlugin({
collections: {
'my-collection-slug': {
adapter: theAdapterToUse, // see docs for the adapter you want to use
},
},
}),
],
// The rest of your config goes here
})
Conditionally Enabling/Disabling
The proper way to conditionally enable/disable this plugin is to use the enabled property.
cloudStoragePlugin({
enabled: process.env.MY_CONDITION === 'true',
collections: {
'my-collection-slug': {
adapter: theAdapterToUse, // see docs for the adapter you want to use
},
},
}),
If the code is included in any way in your config but conditionally disabled in another fashion, you may run into issues such as Webpack Build Error: Can't Resolve 'fs' and 'stream' or similar because the plugin must be run at all times in order to properly extend the webpack config.
Features
Adapter-based Implementation
This plugin supports the following adapters:
However, you can create your own adapter for any third-party service you would like to use.
All adapters are implemented dev directory's Payload Config. See this file for examples.
Plugin options
This plugin is configurable to work across many different Payload collections. A * denotes that the property is required.
| Option | Type | Description |
|---|---|---|
collections * |
Record<string, CollectionOptions> | Object with keys set to the slug of collections you want to enable the plugin for, and values set to collection-specific options. |
enabled |
boolean to conditionally enable/disable plugin. Default: true. |
Collection-specific options:
| Option | Type | Description |
|---|---|---|
adapter * |
Adapter | Pass in the adapter that you'd like to use for this collection. You can also set this field to null for local development if you'd like to bypass cloud storage in certain scenarios and use local storage. |
disableLocalStorage |
boolean |
Choose to disable local storage on this collection. Defaults to true. |
disablePayloadAccessControl |
true |
Set to true to disable Payload's access control. More |
prefix |
string |
Set to media/images to upload files inside media/images folder in the bucket. |
generateFileURL |
GenerateFileURL | Override the generated file URL with one that you create. |
Payload Access Control
Payload ships with access control that runs even on statically served files. The same read access control property on your upload-enabled collections is used, and it allows you to restrict who can request your uploaded files.
To preserve this feature, by default, this plugin keeps all file URLs exactly the same. Your file URLs won't be updated to point directly to your cloud storage source, as in that case, Payload's access control will be completely bypassed and you would need public readability on your cloud-hosted files.
Instead, all uploads will still be reached from the default /:collectionSlug/file/:filename path. This plugin will "pass through" all files that are hosted on your third-party cloud service—with the added benefit of keeping your existing access control in place.
If this does not apply to you (your upload collection has read: () => true or similar) you can disable this functionality by setting disablePayloadAccessControl to true. When this setting is in place, this plugin will update your file URLs to point directly to your cloud host.
Credit
This plugin was created with significant help, and code, from Alex Bechmann and Richard VanBergen. Thank you!!