diff --git a/README.md b/README.md index 715087d..6979216 100644 --- a/README.md +++ b/README.md @@ -108,6 +108,13 @@ Default: `true` > Ignore the optimized output if it is larger than the original file. +### cache + +Type: `boolean`
+Default: `true` + +> Skip optimizing the input if it did not change since the last run. + ### makeAvif / makeWebp Type: `object`
diff --git a/package-lock.json b/package-lock.json index 20aa19a..329c727 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,15 +11,15 @@ "packages/*" ], "devDependencies": { - "@typescript-eslint/eslint-plugin": "^6.9.1", - "@typescript-eslint/parser": "^6.9.1", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", "@vheemstra/imagemin-avifenc": "^2.1.0", "@vheemstra/imagemin-oxipng": "^1.0.0", "@vitest/coverage-c8": "^0.32.2", "@vitest/coverage-istanbul": "^0.34.6", "cross-env": "^7.0.3", "cross-var": "^1.1.0", - "eslint": "^8.52.0", + "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-vitest-globals": "^1.4.0", @@ -32,7 +32,7 @@ "imagemin-svgo": "^10.0.1", "imagemin-webp": "^8.0.0", "npm-run-all": "^4.1.5", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "serve": "^14.2.1", "typescript": "^5.2.2", "vite": "^4.5.0", @@ -894,9 +894,9 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.2.tgz", - "integrity": "sha512-+wvgpDsrB1YqAMdEUCcnTlpfVBH7Vqn6A/NT3D8WVXFIaKMlErPIZT3oCIAVCOtarRpMtelZLqJeU3t7WY6X6g==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", "dev": true, "dependencies": { "ajv": "^6.12.4", @@ -944,14 +944,26 @@ } }, "node_modules/@eslint/js": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.52.0.tgz", - "integrity": "sha512-mjZVbpaeMZludF2fsWLD0Z9gCref1Tk4i9+wddjRvpUNqqcndPkBD09N/Mapey0b3jaXbLm2kICwFv2E64QinA==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@file-cache/core": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@file-cache/core/-/core-1.1.4.tgz", + "integrity": "sha512-UWUzHu67Mz95Z5Gs10mu/tvPVY5vIPdOiqM6+TVbaEdxNZ/9D5ojOwyHor/J09QzQhuXp1tIlKbu6/s1od2NvQ==", + "dependencies": { + "pkg-dir": "^6.0.1" + }, + "engines": { + "node": ">=16.12.0", + "npm": ">=6.14.0" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.13", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", @@ -1214,9 +1226,9 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.14", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.14.tgz", - "integrity": "sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/keyv": { @@ -1276,22 +1288,22 @@ "dev": true }, "node_modules/@types/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==", + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", + "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.9.1.tgz", - "integrity": "sha512-w0tiiRc9I4S5XSXXrMHOWgHgxbrBn1Ro+PmiYhSg2ZVdxrAJtQgzU5o2m1BfP6UOn7Vxcc6152vFjQfmZR4xEg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.11.0.tgz", + "integrity": "sha512-uXnpZDc4VRjY4iuypDBKzW1rz9T5YBBK0snMn8MaTSNd2kMlj50LnLBABELjJiOL5YHk7ZD8hbSpI9ubzqYI0w==", "dev": true, "dependencies": { "@eslint-community/regexpp": "^4.5.1", - "@typescript-eslint/scope-manager": "6.9.1", - "@typescript-eslint/type-utils": "6.9.1", - "@typescript-eslint/utils": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", + "@typescript-eslint/scope-manager": "6.11.0", + "@typescript-eslint/type-utils": "6.11.0", + "@typescript-eslint/utils": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", @@ -1317,15 +1329,15 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.9.1.tgz", - "integrity": "sha512-C7AK2wn43GSaCUZ9do6Ksgi2g3mwFkMO3Cis96kzmgudoVaKyt62yNzJOktP0HDLb/iO2O0n2lBOzJgr6Q/cyg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.11.0.tgz", + "integrity": "sha512-+whEdjk+d5do5nxfxx73oanLL9ghKO3EwM9kBCkUtWMRwWuPaFv9ScuqlYfQ6pAD6ZiJhky7TZ2ZYhrMsfMxVQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "6.9.1", - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/typescript-estree": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", + "@typescript-eslint/scope-manager": "6.11.0", + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/typescript-estree": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0", "debug": "^4.3.4" }, "engines": { @@ -1345,13 +1357,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.9.1.tgz", - "integrity": "sha512-38IxvKB6NAne3g/+MyXMs2Cda/Sz+CEpmm+KLGEM8hx/CvnSRuw51i8ukfwB/B/sESdeTGet1NH1Wj7I0YXswg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.11.0.tgz", + "integrity": "sha512-0A8KoVvIURG4uhxAdjSaxy8RdRE//HztaZdG8KiHLP8WOXSk0vlF7Pvogv+vlJA5Rnjj/wDcFENvDaHb+gKd1A==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1" + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1362,13 +1374,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.9.1.tgz", - "integrity": "sha512-eh2oHaUKCK58qIeYp19F5V5TbpM52680sB4zNSz29VBQPTWIlE/hCj5P5B1AChxECe/fmZlspAWFuRniep1Skg==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.11.0.tgz", + "integrity": "sha512-nA4IOXwZtqBjIoYrJcYxLRO+F9ri+leVGoJcMW1uqr4r1Hq7vW5cyWrA43lFbpRvQ9XgNrnfLpIkO3i1emDBIA==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "6.9.1", - "@typescript-eslint/utils": "6.9.1", + "@typescript-eslint/typescript-estree": "6.11.0", + "@typescript-eslint/utils": "6.11.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, @@ -1389,9 +1401,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.9.1.tgz", - "integrity": "sha512-BUGslGOb14zUHOUmDB2FfT6SI1CcZEJYfF3qFwBeUrU6srJfzANonwRYHDpLBuzbq3HaoF2XL2hcr01c8f8OaQ==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.11.0.tgz", + "integrity": "sha512-ZbEzuD4DwEJxwPqhv3QULlRj8KYTAnNsXxmfuUXFCxZmO6CF2gM/y+ugBSAQhrqaJL3M+oe4owdWunaHM6beqA==", "dev": true, "engines": { "node": "^16.0.0 || >=18.0.0" @@ -1402,13 +1414,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.1.tgz", - "integrity": "sha512-U+mUylTHfcqeO7mLWVQ5W/tMLXqVpRv61wm9ZtfE5egz7gtnmqVIw9ryh0mgIlkKk9rZLY3UHygsBSdB9/ftyw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.11.0.tgz", + "integrity": "sha512-Aezzv1o2tWJwvZhedzvD5Yv7+Lpu1by/U1LZ5gLc4tCx8jUmuSCMioPFRjliN/6SJIvY6HpTtJIWubKuYYYesQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/visitor-keys": "6.9.1", + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/visitor-keys": "6.11.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -1429,17 +1441,17 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.9.1.tgz", - "integrity": "sha512-L1T0A5nFdQrMVunpZgzqPL6y2wVreSyHhKGZryS6jrEN7bD9NplVAyMryUhXsQ4TWLnZmxc2ekar/lSGIlprCA==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.11.0.tgz", + "integrity": "sha512-p23ibf68fxoZy605dc0dQAEoUsoiNoP3MD9WQGiHLDuTSOuqoTsa4oAy+h3KDkTcxbbfOtUjb9h3Ta0gT4ug2g==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", - "@typescript-eslint/scope-manager": "6.9.1", - "@typescript-eslint/types": "6.9.1", - "@typescript-eslint/typescript-estree": "6.9.1", + "@typescript-eslint/scope-manager": "6.11.0", + "@typescript-eslint/types": "6.11.0", + "@typescript-eslint/typescript-estree": "6.11.0", "semver": "^7.5.4" }, "engines": { @@ -1454,12 +1466,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.1.tgz", - "integrity": "sha512-MUaPUe/QRLEffARsmNfmpghuQkW436DvESW+h+M52w0coICHRfD6Np9/K6PdACwnrq1HmuLl+cSPZaJmeVPkSw==", + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.11.0.tgz", + "integrity": "sha512-+SUN/W7WjBr05uRxPggJPSzyB8zUpaYo2hByKasWbqr3PM8AXfZt8UHdNpBS1v9SA62qnSSMF3380SwDqqprgQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "6.9.1", + "@typescript-eslint/types": "6.11.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { @@ -4963,15 +4975,15 @@ } }, "node_modules/eslint": { - "version": "8.52.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.52.0.tgz", - "integrity": "sha512-zh/JHnaixqHZsolRB/w9/02akBk9EPrOs9JwcTP2ek7yL5bVvXuRariiaAjjoJ5DvuwQ1WAE/HsMz+w17YgBCg==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", - "@eslint/eslintrc": "^2.1.2", - "@eslint/js": "8.52.0", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", "@humanwhocodes/config-array": "^0.11.13", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", @@ -8638,6 +8650,96 @@ "node": ">= 6" } }, + "node_modules/pkg-dir": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-6.0.1.tgz", + "integrity": "sha512-C9R+PTCKGA32HG0n5I4JMYkdLL58ZpayVuncQHQrGeKa8o26A4o2x0u6BKekHG+Au0jv5ZW7Xfq1Cj6lm9Ag4w==", + "dependencies": { + "find-up": "^6.1.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/find-up": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-6.3.0.tgz", + "integrity": "sha512-v2ZsoEuVHYy8ZIlYqwPe/39Cy+cFDzp4dXPaxNvkEuouymu+2Jbz0PxpKarJHYJTmv2HWT3O382qY8l4jMWthw==", + "dependencies": { + "locate-path": "^7.1.0", + "path-exists": "^5.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/locate-path": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz", + "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==", + "dependencies": { + "p-locate": "^6.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-limit": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "dependencies": { + "yocto-queue": "^1.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/p-locate": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "dependencies": { + "p-limit": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pkg-dir/node_modules/path-exists": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz", + "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + } + }, + "node_modules/pkg-dir/node_modules/yocto-queue": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", + "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pkg-types": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.3.tgz", @@ -8849,9 +8951,9 @@ } }, "node_modules/prettier": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.0.3.tgz", - "integrity": "sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.1.0.tgz", + "integrity": "sha512-TQLvXjq5IAibjh8EpBIkNKxO749UEWABoiIZehEPiY4GNpVdhaFKqSTu+QrlU6D2dPAfubRmtJTi4K4YkQ5eXw==", "dev": true, "bin": { "prettier": "bin/prettier.cjs" @@ -11782,6 +11884,7 @@ "version": "1.0.12", "license": "MIT", "dependencies": { + "@file-cache/core": "^1.1.4", "chalk": "^5.2.0", "fast-glob": "^3.2.12", "fs-extra": "^11.1.1", diff --git a/package.json b/package.json index 22c89f6..255f2c3 100644 --- a/package.json +++ b/package.json @@ -56,15 +56,15 @@ "postversion": "git push --follow-tags" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "^6.9.1", - "@typescript-eslint/parser": "^6.9.1", + "@typescript-eslint/eslint-plugin": "^6.11.0", + "@typescript-eslint/parser": "^6.11.0", "@vheemstra/imagemin-avifenc": "^2.1.0", "@vheemstra/imagemin-oxipng": "^1.0.0", "@vitest/coverage-c8": "^0.32.2", "@vitest/coverage-istanbul": "^0.34.6", "cross-env": "^7.0.3", "cross-var": "^1.1.0", - "eslint": "^8.52.0", + "eslint": "^8.53.0", "eslint-config-prettier": "^9.0.0", "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-vitest-globals": "^1.4.0", @@ -77,7 +77,7 @@ "imagemin-svgo": "^10.0.1", "imagemin-webp": "^8.0.0", "npm-run-all": "^4.1.5", - "prettier": "^3.0.3", + "prettier": "^3.1.0", "serve": "^14.2.1", "typescript": "^5.2.2", "vite": "^4.5.0", diff --git a/packages/core/package.json b/packages/core/package.json index a1b41f0..7776006 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -61,6 +61,7 @@ "node": ">=16.0.0" }, "dependencies": { + "@file-cache/core": "^1.1.4", "chalk": "^5.2.0", "fast-glob": "^3.2.12", "fs-extra": "^11.1.1", diff --git a/packages/core/src/index.test.ts b/packages/core/src/index.test.ts index cdc8b84..23b3689 100644 --- a/packages/core/src/index.test.ts +++ b/packages/core/src/index.test.ts @@ -1368,6 +1368,7 @@ describe('logErrors', () => { * dist/images/opaque-1.png.avif */ +// TODO: add tests using cache // TODO: expand after-build checks describe.skipIf(skipBuilds)('viteImagemin', () => { @@ -1452,6 +1453,7 @@ describe.skipIf(skipBuilds)('viteImagemin', () => { // skipIfLargerThan: 'optimized', // skipIfLargerThan: 'smallest', }, + cache: false, } const testConfig = getBuildConfig(viteImagemin(options), { @@ -1518,6 +1520,7 @@ describe.skipIf(skipBuilds)('viteImagemin', () => { skipIfLargerThan: 'smallest' as const, }, logger: mockLogger, + cache: false, } const testConfig = getBuildConfig(viteImagemin(options), { @@ -1574,6 +1577,7 @@ describe.skipIf(skipBuilds)('viteImagemin', () => { ], }, logger: mockLogger, + cache: false, } const testConfig = getBuildConfig(viteImagemin(options), { @@ -1619,6 +1623,7 @@ describe.skipIf(skipBuilds)('viteImagemin', () => { plugins: { gif: [mockPlugin], }, + cache: false, } const testConfig = getBuildConfig(viteImagemin(options), { @@ -1649,6 +1654,7 @@ describe.skipIf(skipBuilds)('viteImagemin', () => { plugins: { gif: [mockPlugin], }, + cache: false, } const testConfig = getBuildConfig(viteImagemin(options), { @@ -1684,6 +1690,7 @@ describe.skipIf(skipBuilds)('viteImagemin', () => { }), ], }, + cache: false, } const testConfig = getBuildConfig(viteImagemin(options), { @@ -1714,6 +1721,7 @@ describe.skipIf(skipBuilds)('viteImagemin', () => { plugins: { none: [mockPlugin], }, + cache: false, } const testConfig = getBuildConfig(viteImagemin(options), { diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index dd2b147..9278761 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -1,5 +1,10 @@ import path from 'node:path' -import { lstatSync, readdirSync, unlinkSync /*, rmSync */ } from 'node:fs' +import { + existsSync, + lstatSync, + readdirSync, + unlinkSync /*, rmSync */, +} from 'node:fs' import { readFile, writeFile } from 'node:fs/promises' import { Buffer } from 'node:buffer' import { performance } from 'node:perf_hooks' @@ -7,6 +12,8 @@ import chalk from 'chalk' import { normalizePath, createFilter } from 'vite' import imagemin from 'imagemin' import isAPNG from 'is-apng' +import { createCache } from '@file-cache/core' + import { isFunction, isBoolean, @@ -34,6 +41,7 @@ import type { ProcessResult, ProcessFileReturn, } from './typings' +import { CacheInterface } from '@file-cache/core/mjs/CacheInterface' export const parsePlugins = (rawPlugins: PluginsConfig) => { let plugins: false | ResolvedPluginsConfig = false @@ -84,8 +92,8 @@ export const parseOptions = ( false === _options.makeAvif.skipIfLargerThan ? _options.makeAvif.skipIfLargerThan : isString(_options.makeAvif.skipIfLargerThan) - ? _options.makeAvif.skipIfLargerThan - : 'optimized', + ? _options.makeAvif.skipIfLargerThan + : 'optimized', } } } @@ -104,8 +112,8 @@ export const parseOptions = ( false === _options.makeWebp.skipIfLargerThan ? _options.makeWebp.skipIfLargerThan : isString(_options.makeWebp.skipIfLargerThan) - ? _options.makeWebp.skipIfLargerThan - : 'optimized', + ? _options.makeWebp.skipIfLargerThan + : 'optimized', } } } @@ -131,6 +139,7 @@ export const parseOptions = ( skipIfLarger: isBoolean(_options?.skipIfLarger) ? _options.skipIfLarger : true, + cache: isBoolean(_options?.cache) ? _options.cache : true, plugins, makeAvif, makeWebp, @@ -179,6 +188,7 @@ export async function processFile({ precisions, bytesDivider, sizeUnit, + cache, }: ProcessFileParams): ProcessFileReturn { // const start = performance.now() @@ -200,6 +210,24 @@ export async function processFile({ }) as Promise } + if (cache) { + const result = await cache.getAndUpdateCache(baseDir + filePathFrom) + if (!result.changed) { + const outputsStillExist = fileToStack.every(item => { + return existsSync(baseDir + item.toPath) + }) + + if (outputsStillExist) { + return Promise.reject({ + oldPath: filePathFrom, + newPath: '', + error: '', + errorType: 'cache', + }) as Promise + } + } + } + let oldBuffer: Buffer let oldSize = 0 @@ -526,8 +554,8 @@ export function logResults( file.ratio < 0 ? chalk.green(basenameTo) : file.ratio > 0 - ? chalk.yellow(basenameTo) - : basenameTo, + ? chalk.yellow(basenameTo) + : basenameTo, ' '.repeat( maxPathLength - bulletLength - file.newPath.length + spaceLength, ), @@ -542,8 +570,8 @@ export function logResults( file.ratio < 0 ? chalk.green(file.ratioString) : file.ratio > 0 - ? chalk.red(file.ratioString) - : file.ratioString, + ? chalk.red(file.ratioString) + : file.ratioString, chalk.dim(' │ '), // Duration @@ -612,6 +640,9 @@ export function logErrors( file.error, ) break + case 'cache': + logArray.push(chalk.black.bgBlue(' CACHED '), ' ', file.error) + break case 'warning': logArray.push( chalk.bgYellow(' WARNING '), @@ -653,6 +684,9 @@ export function logErrors( file.error, ) break + case 'cache': + logArray.push(chalk.black.bgBlue(' CACHED '), ' ', file.error) + break case 'warning': logArray.push( chalk.bgYellow(' WARNING '), @@ -716,6 +750,8 @@ export default function viteImagemin(_options: ConfigOptions): PluginOption { let hadFilesToProcess = false // const mtimeCache = new Map() + let cache: CacheInterface + return { name: 'vite-plugin-imagemin', enforce: 'post', @@ -746,6 +782,17 @@ export default function viteImagemin(_options: ConfigOptions): PluginOption { const baseDir = `${root}/` const rootRE = new RegExp(`^${escapeRegExp(baseDir)}`) + // Create cache for this run + cache = (await createCache({ + noCache: options.cache === false, + mode: 'content', + keys: [ + () => { + return JSON.stringify(options) + }, + ], + })) as CacheInterface + // Get all input files to (potentially) process const files = getAllFiles(processDir, logger) .filter(filter) @@ -852,6 +899,7 @@ export default function viteImagemin(_options: ConfigOptions): PluginOption { precisions, bytesDivider, sizeUnit, + cache, }), ), ) as Promise @@ -965,6 +1013,9 @@ export default function viteImagemin(_options: ConfigOptions): PluginOption { logResults(processedFiles[k], logger, maxLengths) }) + // Write cache state to file for persistence + await cache.reconcile() + Object.keys(erroredFiles) .sort((a, b) => a.localeCompare(b)) // TODO: sort by (sub)folder and depth? .forEach(k => { @@ -989,16 +1040,18 @@ export default function viteImagemin(_options: ConfigOptions): PluginOption { totalDuration.length, ) + 2 const totalRatio = (totalSize.to / totalSize.from - 1) * 100 - const totalRatioString = - totalRatio < 0 + + const totalRatioString = isNaN(totalRatio) + ? '0 %' + : totalRatio < 0 ? chalk.green( `-${Math.abs(totalRatio).toFixed(precisions.ratio)} %`, ) : totalRatio > 0 - ? chalk.red( - `+${Math.abs(totalRatio).toFixed(precisions.ratio)} %`, - ) - : `${Math.abs(totalRatio).toFixed(precisions.ratio)} %` + ? chalk.red( + `+${Math.abs(totalRatio).toFixed(precisions.ratio)} %`, + ) + : `${Math.abs(totalRatio).toFixed(precisions.ratio)} %` logger.info('') diff --git a/packages/core/src/typings.d.ts b/packages/core/src/typings.d.ts index cb11cdc..7abe90d 100644 --- a/packages/core/src/typings.d.ts +++ b/packages/core/src/typings.d.ts @@ -1,5 +1,6 @@ import type { Logger as ViteLogger, FilterPattern } from 'vite' import type { Plugin as ImageminPlugin } from 'imagemin' +import { CacheInterface } from '@file-cache/core/mjs/CacheInterface' // type Required = { // [P in keyof T]-?: T[P] @@ -79,6 +80,12 @@ export interface ConfigOptions { */ verbose?: boolean + /** + * Only optimize contents if it was updated. + * @default true + */ + cache?: boolean + /** * Only use optimized contents if smaller than original. * @default true @@ -128,6 +135,7 @@ export interface ResolvedConfigOptions { onlyAssets: boolean verbose: boolean skipIfLarger: boolean + cache: boolean plugins: ResolvedPluginsConfig makeAvif: false | ResolvedMakeConfigOptions makeWebp: false | ResolvedMakeConfigOptions @@ -162,6 +170,7 @@ export type ProcessFileParams = { } bytesDivider: number sizeUnit: string + cache: CacheInterface } export type ProcessedFile = { diff --git a/packages/playground/vite.config.ts b/packages/playground/vite.config.ts index 97f4fa6..3eddf1d 100644 --- a/packages/playground/vite.config.ts +++ b/packages/playground/vite.config.ts @@ -51,6 +51,7 @@ export default defineConfig({ // skipIfLargerThan: 'optimized', // default // skipIfLargerThan: 'smallest', }, + // cache: false, }), ], })