diff --git a/CHANGELOG.md b/CHANGELOG.md index 73f5ac6f..815802bc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Changelog +- **Added** `output` field for cached tasks: archives output files after a successful run and restores them on cache hit. Defaults to automatically tracking files the task writes; accepts globs (e.g. `"dist/**"`), `{ "auto": true }`, and negative patterns (`"!dist/cache/**"`) ([#321](https://github.com/voidzero-dev/vite-task/pull/321)) - **Changed** Arguments passed after a task name (e.g. `vp run test some-filter`) are now forwarded only to that task. Tasks pulled in via `dependsOn` no longer receive them ([#324](https://github.com/voidzero-dev/vite-task/issues/324)) - **Fixed** Windows file access tracking no longer panics when a task touches malformed paths that cannot be represented as workspace-relative inputs ([#330](https://github.com/voidzero-dev/vite-task/pull/330)) - **Fixed** `vp run --cache` now supports running without a task specifier and opens the interactive task selector, matching bare `vp run` behavior ([#312](https://github.com/voidzero-dev/vite-task/pull/313)) diff --git a/Cargo.lock b/Cargo.lock index bf11ea1b..c10de4e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -50,21 +50,6 @@ version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" -[[package]] -name = "anstream" -version = "0.6.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" -dependencies = [ - "anstyle", - "anstyle-parse 0.2.7", - "anstyle-query", - "anstyle-wincon", - "colorchoice", - "is_terminal_polyfill", - "utf8parse", -] - [[package]] name = "anstream" version = "1.0.0" @@ -72,7 +57,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d" dependencies = [ "anstyle", - "anstyle-parse 1.0.0", + "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", @@ -82,18 +67,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" - -[[package]] -name = "anstyle-parse" -version = "0.2.7" +version = "1.0.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" -dependencies = [ - "utf8parse", -] +checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000" [[package]] name = "anstyle-parse" @@ -126,9 +102,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.101" +version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f0e0fee31ef5ed1ba1316088939cea399010ed7731dba877ed44aeb407a75ea" +checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" [[package]] name = "arrayvec" @@ -161,9 +137,13 @@ dependencies = [ [[package]] name = "assertables" -version = "9.8.4" +version = "9.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dcd1f7f2f608b9a888a851f234086946c2ca1dfeadf1431c5082fee0942eeb6" +checksum = "627b0b35c347252505ec6c078827dfcecc4e9dcfa931c54c54066f9245ac752b" +dependencies = [ + "regex", + "walkdir", +] [[package]] name = "async-trait" @@ -218,7 +198,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -255,9 +235,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "block-buffer" @@ -279,9 +259,9 @@ dependencies = [ [[package]] name = "bon" -version = "3.8.2" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234655ec178edd82b891e262ea7cf71f6584bcd09eff94db786be23f1821825c" +checksum = "f47dbe92550676ee653353c310dfb9cf6ba17ee70396e1f7cf0a2020ad49b2fe" dependencies = [ "bon-macros", "rustversion", @@ -289,9 +269,9 @@ dependencies = [ [[package]] name = "bon-macros" -version = "3.8.2" +version = "3.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89ec27229c38ed0eb3c0feee3d2c1d6a4379ae44f418a29a658890e062d8f365" +checksum = "519bd3116aeeb42d5372c29d982d16d0170d3d4a5ed85fc7dd91642ffff3c67c" dependencies = [ "darling 0.23.0", "ident_case", @@ -328,9 +308,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.1" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" dependencies = [ "allocator-api2", ] @@ -405,11 +385,13 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.55" +version = "1.2.60" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b26a0954ae34af09b50f0de26458fa95369a0d478d8236d3f93082b219bd29" +checksum = "43c5703da9466b66a946814e1adf53ea2c90f10063b86290cc9eb67ce3478a20" dependencies = [ "find-msvc-tools", + "jobserver", + "libc", "shlex", ] @@ -453,9 +435,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.5.57" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6899ea499e3fb9305a65d5ebf6e3d2248c5fab291f300ad0a704fbe142eae31a" +checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" dependencies = [ "clap_builder", "clap_derive", @@ -463,11 +445,11 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.57" +version = "4.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b12c8b680195a62a8364d16b8447b01b6c2c8f9aaf68bee653be34d4245e238" +checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" dependencies = [ - "anstream 0.6.21", + "anstream", "anstyle", "clap_lex", "strsim", @@ -475,9 +457,9 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.55" +version = "4.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" +checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -487,9 +469,9 @@ dependencies = [ [[package]] name = "clap_lex" -version = "0.7.7" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" +checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "color-eyre" @@ -520,9 +502,9 @@ dependencies = [ [[package]] name = "colorchoice" -version = "1.0.4" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" +checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "compact_str" @@ -541,11 +523,12 @@ dependencies = [ [[package]] name = "const_format" -version = "0.2.35" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" +checksum = "4481a617ad9a412be3b97c5d403fef8ed023103368908b9c50af598ff467cc1e" dependencies = [ "const_format_proc_macros", + "konst", ] [[package]] @@ -647,7 +630,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "crossterm_winapi", "derive_more", "document-features", @@ -737,7 +720,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e0b1fab2ae45819af2d0731d60f2afe17227ebb1a1538a236da84c93e9a60162" dependencies = [ "dispatch2", - "nix 0.31.1", + "nix 0.31.2", "windows-sys 0.61.2", ] @@ -853,9 +836,9 @@ checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4" [[package]] name = "deranged" -version = "0.5.5" +version = "0.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" dependencies = [ "powerfmt", ] @@ -944,11 +927,11 @@ dependencies = [ [[package]] name = "dispatch2" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "block2", "libc", "objc2", @@ -998,26 +981,20 @@ checksum = "55dd888a213fc57e957abf2aa305ee3e8a28dbe05687a251f33b637cd46b0070" [[package]] name = "env_filter" -version = "0.1.4" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bf3c259d255ca70051b30e2e95b5446cdb8949ac4cd22c0d7fd634d89f568e2" +checksum = "32e90c2accc4b07a8456ea0debdc2e7587bdd890680d71173a15d4ae604f6eef" dependencies = [ "log", ] -[[package]] -name = "env_home" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" - [[package]] name = "env_logger" -version = "0.11.8" +version = "0.11.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c863f0904021b108aa8b2f55046443e6b1ebde8fd4a15c399893aae4fa069f" +checksum = "0621c04f2196ac3f488dd583365b9c09be011a4ab8b9f37248ffcc8f6198b56a" dependencies = [ - "anstream 0.6.21", + "anstream", "anstyle", "env_filter", "log", @@ -1047,9 +1024,9 @@ checksum = "5692dd7b5a1978a5aeb0ce83b7655c58ca8efdcb79d21036ea249da95afec2c6" [[package]] name = "euclid" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" dependencies = [ "num-traits", ] @@ -1088,9 +1065,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "filedescriptor" @@ -1212,7 +1189,7 @@ dependencies = [ "which", "winapi", "wincode", - "winsafe 0.0.24", + "winsafe", ] [[package]] @@ -1264,7 +1241,7 @@ dependencies = [ "widestring", "winapi", "wincode", - "winsafe 0.0.24", + "winsafe", ] [[package]] @@ -1291,7 +1268,7 @@ version = "0.0.0" dependencies = [ "allocator-api2", "assert2", - "bitflags 2.10.0", + "bitflags 2.11.1", "bstr", "bytemuck", "ctor", @@ -1333,9 +1310,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1348,9 +1325,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", @@ -1358,15 +1335,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", @@ -1375,15 +1352,15 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -1392,21 +1369,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1416,7 +1393,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -1449,19 +1425,19 @@ checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", ] [[package]] name = "getrandom" -version = "0.4.1" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555" dependencies = [ "cfg-if", "libc", - "r-efi", + "r-efi 6.0.0", "wasip2", "wasip3", ] @@ -1500,6 +1476,12 @@ dependencies = [ "foldhash 0.2.0", ] +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" + [[package]] name = "hashlink" version = "0.10.0" @@ -1553,12 +1535,12 @@ checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.0", "serde", "serde_core", ] @@ -1578,7 +1560,7 @@ version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd5b3eaf1a28b758ac0faa5a4254e8ab2705605496f1b1f3fbbc3988ad73d199" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "inotify-sys", "libc", ] @@ -1594,9 +1576,9 @@ dependencies = [ [[package]] name = "instability" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357b7205c6cd18dd2c86ed312d1e70add149aea98e7ef72b9fdf0270e555c11d" +checksum = "5eb2d60ef19920a3a9193c3e371f726ec1dafc045dac788d0fb3704272458971" dependencies = [ "darling 0.23.0", "indoc", @@ -1648,15 +1630,25 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] [[package]] name = "js-sys" -version = "0.3.85" +version = "0.3.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c942ebf8e95485ca0d52d97da7c5a2c387d0e7f0ba4c35e93bfcaee045955b3" +checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca" dependencies = [ "once_cell", "wasm-bindgen", @@ -1673,15 +1665,30 @@ dependencies = [ [[package]] name = "kasuari" -version = "0.4.11" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fe90c1150662e858c7d5f945089b7517b0a80d8bf7ba4b1b5ffc984e7230a5b" +checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" dependencies = [ "hashbrown 0.16.1", "portable-atomic", "thiserror 2.0.18", ] +[[package]] +name = "konst" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "128133ed7824fcd73d6e7b17957c5eb7bacb885649bd8c69708b2331a10bcefb" +dependencies = [ + "konst_macro_rules", +] + +[[package]] +name = "konst_macro_rules" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4933f3f57a8e9d9da04db23fb153356ecaf00cbd14aee46279c33dc80925c37" + [[package]] name = "kqueue" version = "1.1.1" @@ -1722,9 +1729,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.180" +version = "0.2.185" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" +checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f" [[package]] name = "libloading" @@ -1738,13 +1745,14 @@ dependencies = [ [[package]] name = "libredox" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "libc", - "redox_syscall 0.7.0", + "plain", + "redox_syscall 0.7.4", ] [[package]] @@ -1764,7 +1772,7 @@ version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "14e6ba06f0ade6e504aff834d7c34298e5155c6baca353cc6a4aaff2f9fd7f33" dependencies = [ - "anstream 1.0.0", + "anstream", "anstyle", "clap", "escape8259", @@ -1782,18 +1790,18 @@ dependencies = [ [[package]] name = "line-clipping" -version = "0.3.5" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4de44e98ddbf09375cbf4d17714d18f39195f4f4894e8524501726fd9a8a4a" +checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] [[package]] name = "linux-raw-sys" -version = "0.11.0" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" +checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litrs" @@ -1818,9 +1826,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru" -version = "0.16.3" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" dependencies = [ "hashbrown 0.16.1", ] @@ -1866,9 +1874,9 @@ checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memmap2" -version = "0.9.9" +version = "0.9.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" +checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" dependencies = [ "libc", ] @@ -1915,9 +1923,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "log", @@ -1975,7 +1983,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "cfg_aliases 0.1.1", "libc", @@ -1987,7 +1995,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -2000,7 +2008,7 @@ version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -2009,11 +2017,11 @@ dependencies = [ [[package]] name = "nix" -version = "0.31.1" +version = "0.31.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225e7cfe711e0ba79a68baeddb2982723e4235247aefce1482f2f16c27865b66" +checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "cfg-if", "cfg_aliases 0.2.1", "libc", @@ -2035,7 +2043,7 @@ version = "8.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "fsevent-sys", "inotify", "kqueue", @@ -2053,7 +2061,7 @@ version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42b8cfee0e339a0337359f3c88165702ac6e600dc01c0cc9579a92d62b08477a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] [[package]] @@ -2152,9 +2160,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.0" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" +checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" [[package]] name = "num-derive" @@ -2218,9 +2226,9 @@ dependencies = [ [[package]] name = "objc2" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c2599ce0ec54857b29ce62166b0ed9b4f6f1a70ccc9a71165b6154caca8c05" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ "objc2-encode", ] @@ -2242,9 +2250,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.21.3" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] name = "once_cell_polyfill" @@ -2302,9 +2310,9 @@ dependencies = [ [[package]] name = "owo-colors" -version = "4.2.3" +version = "4.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c6901729fa79e91a0913333229e9ca5dc725089d1c363b2f4b4760709dc4a52" +checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" dependencies = [ "supports-color 2.1.0", "supports-color 3.0.2", @@ -2465,7 +2473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -2492,21 +2500,21 @@ dependencies = [ [[package]] name = "pin-project-lite" -version = "0.2.16" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "pin-utils" -version = "0.1.0" +name = "pkg-config" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] -name = "pkg-config" -version = "0.3.32" +name = "plain" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "pori" @@ -2581,9 +2589,9 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ "toml_edit", ] @@ -2659,11 +2667,17 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -2672,9 +2686,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.2" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", @@ -2738,7 +2752,7 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5ef8dea09a92caaf73bff7adb70b76162e5937524058a7e5bff37869cbbec293" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "compact_str", "hashbrown 0.16.1", "indoc", @@ -2790,7 +2804,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7dbfa023cd4e604c2553483820c5fe8aa9d71a42eea5aa77c6e7f35756612db" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "hashbrown 0.16.1", "indoc", "instability", @@ -2805,9 +2819,9 @@ dependencies = [ [[package]] name = "rayon" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" dependencies = [ "either", "rayon-core", @@ -2829,16 +2843,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] [[package]] name = "redox_syscall" -version = "0.7.0" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f3fe0889e69e2ae9e41f4d6c4c0181701d00e4697b356fb1f74173a5e0ee27" +checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", ] [[package]] @@ -2897,9 +2911,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.9" +version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a96887878f22d7bad8a3b6dc5b7440e0ada9a245242924394987b21cf2210a4c" +checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rusqlite" @@ -2907,7 +2921,7 @@ version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "165ca6e57b20e1351573e3729b958bc62f0e48025386970b6e4d29e7a7e71f3f" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -2923,9 +2937,9 @@ checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -2942,16 +2956,16 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ - "semver 1.0.27", + "semver 1.0.28", ] [[package]] name = "rustix" -version = "1.1.3" +version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys", @@ -3004,9 +3018,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "semver-parser" @@ -3060,9 +3074,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.0.4" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8bbf91e5a4d6315eee45e704372590b30e260ee83af6639d64557f51b067776" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -3084,9 +3098,9 @@ dependencies = [ [[package]] name = "serial2" -version = "0.2.33" +version = "0.2.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cc76fa68e25e771492ca1e3c53d447ef0be3093e05cd3b47f4b712ba10c6f3c" +checksum = "fcdbc46aa3882ec3d48ec2b5abcb4f0d863a13d7599265f3faa6d851f23c12f3" dependencies = [ "cfg-if", "libc", @@ -3133,7 +3147,7 @@ dependencies = [ "libc", "log", "nix 0.23.2", - "rand 0.8.5", + "rand 0.8.6", "win-sys", ] @@ -3188,9 +3202,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "similar" @@ -3233,12 +3247,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3345,9 +3359,9 @@ checksum = "43d0e35dc7d73976a53c7e6d7d177ef804a0c0ee774ec77bcc520c2216fd7cbe" [[package]] name = "tar" -version = "0.4.44" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +checksum = "22692a6476a21fa75fdfc11d452fda482af402c008cdbaf3476414e122040973" dependencies = [ "filetime", "libc", @@ -3356,12 +3370,12 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.25.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0136791f7c95b1f6dd99f9cc786b91bb81c3800b639b3478e561ddb7be95e5f1" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", - "getrandom 0.4.1", + "getrandom 0.4.2", "once_cell", "rustix", "windows-sys 0.61.2", @@ -3378,12 +3392,12 @@ dependencies = [ [[package]] name = "terminal_size" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b8cb979cb11c32ce1603f8137b22262a9d131aaa5c37b5678025f22b8becd0" +checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874" dependencies = [ "rustix", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3415,7 +3429,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" dependencies = [ "anyhow", "base64", - "bitflags 2.10.0", + "bitflags 2.11.1", "fancy-regex", "filedescriptor", "finl_unicode", @@ -3451,9 +3465,9 @@ dependencies = [ [[package]] name = "test-log" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37d53ac171c92a39e4769491c4b4dde7022c60042254b5fc044ae409d34a24d4" +checksum = "2f46bf474f0a4afebf92f076d54fd5e63423d9438b8c278a3d2ccb0f47f7cdb3" dependencies = [ "env_logger", "test-log-macros", @@ -3461,16 +3475,26 @@ dependencies = [ ] [[package]] -name = "test-log-macros" -version = "0.2.19" +name = "test-log-core" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be35209fd0781c5401458ab66e4f98accf63553e8fae7425503e92fdd319783b" +checksum = "37d4d41320b48bc4a211a9021678fcc0c99569b594ea31c93735b8e517102b4c" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] +[[package]] +name = "test-log-macros" +version = "0.2.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9beb9249a81e430dffd42400a49019bcf548444f1968ff23080a625de0d4d320" +dependencies = [ + "syn 2.0.117", + "test-log-core", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -3543,9 +3567,9 @@ checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" [[package]] name = "tokio" -version = "1.49.0" +version = "1.52.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" +checksum = "b67dee974fe86fd92cc45b7a95fdd2f99a36a6d7b0d431a231178d3d670bbcc6" dependencies = [ "bytes", "libc", @@ -3560,9 +3584,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -3602,10 +3626,10 @@ dependencies = [ "indexmap", "serde_core", "serde_spanned", - "toml_datetime", + "toml_datetime 0.7.5+spec-1.1.0", "toml_parser", "toml_writer", - "winnow", + "winnow 0.7.15", ] [[package]] @@ -3617,32 +3641,41 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" -version = "0.23.10+spec-1.0.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap", - "toml_datetime", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow", + "winnow 1.0.1", ] [[package]] name = "toml_parser" -version = "1.0.7+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "247eaa3197818b831697600aadf81514e577e0cba5eab10f7e064e78ae154df1" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow", + "winnow 1.0.1", ] [[package]] name = "toml_writer" -version = "1.0.6+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab16f14aed21ee8bfd8ec22513f7287cd4a91aa92e44edfe2c17ddd004e92607" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tracing" @@ -3699,9 +3732,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.22" +version = "0.3.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" dependencies = [ "matchers", "nu-ansi-term", @@ -3740,9 +3773,9 @@ dependencies = [ [[package]] name = "tui-term" -version = "0.3.1" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f4d2473af6ae50523181a971dd8c8557416ece8ba4fcd2d63331be6f73759c" +checksum = "a338ded85dbe7f9ea2298321d126244f54e531e2b2006b97abdab8e47d6f3c88" dependencies = [ "ratatui-core", "ratatui-widgets", @@ -3755,14 +3788,14 @@ version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" dependencies = [ - "rand 0.9.2", + "rand 0.9.4", ] [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "40ce102ab67701b8526c123c1bab5cbe42d7040ccfd0f64af1a385808d2f43de" [[package]] name = "ucd-trie" @@ -3772,15 +3805,15 @@ checksum = "2896d95c02a80c6d6a5d6e953d479f5ddf2dfdb6a244441010e373ac0fb88971" [[package]] name = "unicode-ident" -version = "1.0.23" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "537dd038a89878be9b64dd4bd1b260315c1bb94f4d784956b81e27a088d9a09e" +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-truncate" @@ -3822,12 +3855,12 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.20.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee48d38b119b0cd71fe4141b30f5ba9c7c5d9f4e7a3a8b4a674e4b6ef789976f" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "atomic", - "getrandom 0.3.4", + "getrandom 0.4.2", "js-sys", "wasm-bindgen", ] @@ -3949,12 +3982,14 @@ dependencies = [ "rustc-hash", "serde", "serde_json", + "tar", "tempfile", "thiserror 2.0.18", "tokio", "tokio-util", "tracing", "twox-hash", + "uuid", "vite_path", "vite_select", "vite_str", @@ -3964,6 +3999,7 @@ dependencies = [ "wax", "winapi", "wincode", + "zstd", ] [[package]] @@ -4146,11 +4182,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -4159,14 +4195,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.108" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64024a30ec1e37399cf85a7ffefebdb72205ca1c972291c51512360d90bd8566" +checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89" dependencies = [ "cfg-if", "once_cell", @@ -4177,9 +4213,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.108" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "008b239d9c740232e71bd39e8ef6429d27097518b6b30bdf9086833bd5b6d608" +checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4187,9 +4223,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.108" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5256bae2d58f54820e6490f9839c49780dff84c65aeab9e772f15d5f0e913a55" +checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904" dependencies = [ "bumpalo", "proc-macro2", @@ -4200,9 +4236,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.108" +version = "0.2.118" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f01b580c9ac74c8d8f0c0e4afb04eeef2acf145458e52c03845ee9cd23e3d12" +checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129" dependencies = [ "unicode-ident", ] @@ -4235,10 +4271,10 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.10.0", + "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap", - "semver 1.0.27", + "semver 1.0.28", ] [[package]] @@ -4340,14 +4376,12 @@ dependencies = [ [[package]] name = "which" -version = "8.0.0" +version = "8.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" +checksum = "81995fafaaaf6ae47a7d0cc83c67caf92aeb7e5331650ae6ff856f7c0c60c459" dependencies = [ - "env_home", - "rustix", + "libc", "tracing", - "winsafe 0.0.19", ] [[package]] @@ -4398,9 +4432,9 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "wincode" -version = "0.5.2" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2f42dd20febad683d07044c5f543e57f822512ebebaf2c827705c99a0ad4575" +checksum = "b4c754f1fc41250f2f742a27ba0fcc9f73df1dec23f6878490770855d43c322d" dependencies = [ "pastey", "proc-macro2", @@ -4411,9 +4445,9 @@ dependencies = [ [[package]] name = "wincode-derive" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fca057fc9a13dd19cdb64ef558635d43c42667c0afa1ae7915ea1fa66993fd1a" +checksum = "3e070787599c7c067b89598cd3eda440cca1b69eda9e0ff7c725fc8679ce9eb4" dependencies = [ "darling 0.21.3", "proc-macro2", @@ -4555,9 +4589,15 @@ checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" -version = "0.7.14" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" + +[[package]] +name = "winnow" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09dac053f1cd375980747450bfc7250c264eaae0583872e845c0c7cd578872b5" dependencies = [ "memchr", ] @@ -4571,12 +4611,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "winsafe" -version = "0.0.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" - [[package]] name = "winsafe" version = "0.0.24" @@ -4592,6 +4626,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -4641,7 +4681,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.10.0", + "bitflags 2.11.1", "indexmap", "log", "serde", @@ -4663,7 +4703,7 @@ dependencies = [ "id-arena", "indexmap", "log", - "semver 1.0.27", + "semver 1.0.28", "serde", "serde_derive", "serde_json", @@ -4695,18 +4735,18 @@ checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" [[package]] name = "zerocopy" -version = "0.8.39" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db6d35d663eadb6c932438e763b262fe1a70987f9ae936e60158176d710cae4a" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.39" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4122cd3169e94605190e77839c9a40d40ed048d305bfdc146e7df40ab0f3e517" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -4715,6 +4755,34 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.20" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4de98dfa5d5b7fef4ee834d0073d560c9ca7b6c46a71d058c48db7960f8cfaf7" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/Cargo.toml b/Cargo.toml index 85c0f40f..51034947 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -160,6 +160,7 @@ winsafe = { version = "0.0.24", features = ["kernel"] } xxhash-rust = { version = "0.8.15", features = ["const_xxh3"] } ntest = "0.9.5" terminal_size = "0.4" +zstd = "0.13" [workspace.metadata.cargo-shear] ignored = [ diff --git a/crates/vite_task/Cargo.toml b/crates/vite_task/Cargo.toml index 7ba8f2ff..f57298c5 100644 --- a/crates/vite_task/Cargo.toml +++ b/crates/vite_task/Cargo.toml @@ -30,10 +30,12 @@ rustc-hash = { workspace = true } serde = { workspace = true, features = ["derive", "rc"] } serde_json = { workspace = true } thiserror = { workspace = true } +tar = { workspace = true } tokio = { workspace = true, features = ["rt-multi-thread", "io-std", "io-util", "macros", "sync"] } tokio-util = { workspace = true } tracing = { workspace = true } twox-hash = { workspace = true } +uuid = { workspace = true, features = ["v4"] } vite_path = { workspace = true } vite_select = { workspace = true } vite_str = { workspace = true } @@ -41,6 +43,7 @@ vite_task_graph = { workspace = true } vite_task_plan = { workspace = true } vite_workspace = { workspace = true } wax = { workspace = true } +zstd = { workspace = true } [dev-dependencies] tempfile = { workspace = true } diff --git a/crates/vite_task/docs/task-cache.md b/crates/vite_task/docs/task-cache.md index 440901cc..d6c75a91 100644 --- a/crates/vite_task/docs/task-cache.md +++ b/crates/vite_task/docs/task-cache.md @@ -92,7 +92,7 @@ The cache entry key uniquely identifies a command execution context: ```rust pub struct CacheEntryKey { pub spawn_fingerprint: SpawnFingerprint, - pub input_config: ResolvedInputConfig, + pub input_config: ResolvedGlobConfig, } ``` @@ -303,7 +303,7 @@ Cache entries are serialized using `bincode` for efficient storage. │ ────────────────────── │ │ CacheEntryKey { │ │ spawn_fingerprint: SpawnFingerprint { ... }, │ -│ input_config: ResolvedInputConfig { ... }, │ +│ input_config: ResolvedGlobConfig { ... }, │ │ } │ │ ExecutionCacheKey::UserTask { │ │ task_name: "build", │ diff --git a/crates/vite_task/src/session/cache/archive.rs b/crates/vite_task/src/session/cache/archive.rs new file mode 100644 index 00000000..9a4982ae --- /dev/null +++ b/crates/vite_task/src/session/cache/archive.rs @@ -0,0 +1,61 @@ +//! Output archive creation and extraction using tar + zstd compression. + +use std::fs::File; + +use vite_path::{AbsolutePath, RelativePathBuf}; + +/// Create a tar.zst archive from workspace-relative output file paths. +/// +/// Files that no longer exist are silently skipped (the task may delete +/// temporary files during execution). +/// +/// # Errors +/// +/// Returns an error if creating the archive file or adding entries fails. +pub fn create_output_archive( + workspace_root: &AbsolutePath, + output_files: &[RelativePathBuf], + archive_path: &AbsolutePath, +) -> anyhow::Result<()> { + let file = File::create(archive_path.as_path())?; + let encoder = zstd::Encoder::new(file, 0)?.auto_finish(); + let mut builder = tar::Builder::new(encoder); + + for rel_path in output_files { + let abs_path = workspace_root.join(rel_path); + // Skip files that no longer exist (task may delete temp files) + if !abs_path.as_path().exists() { + continue; + } + let metadata = std::fs::metadata(abs_path.as_path())?; + if metadata.is_file() { + let mut file = File::open(abs_path.as_path())?; + let mut header = tar::Header::new_gnu(); + header.set_metadata(&metadata); + header.set_cksum(); + builder.append_data(&mut header, rel_path.as_str(), &mut file)?; + } + } + + builder.finish()?; + Ok(()) +} + +/// Extract a tar.zst archive, restoring files relative to workspace root. +/// +/// Parent directories are created automatically. Existing files are overwritten. +/// +/// # Errors +/// +/// Returns an error if opening the archive or extracting entries fails. +pub fn extract_output_archive( + workspace_root: &AbsolutePath, + archive_path: &AbsolutePath, +) -> anyhow::Result<()> { + let file = File::open(archive_path.as_path())?; + let decoder = zstd::Decoder::new(file)?; + let mut archive = tar::Archive::new(decoder); + + archive.unpack(workspace_root.as_path())?; + Ok(()) +} diff --git a/crates/vite_task/src/session/cache/display.rs b/crates/vite_task/src/session/cache/display.rs index ec6bf3d3..9dbcabd5 100644 --- a/crates/vite_task/src/session/cache/display.rs +++ b/crates/vite_task/src/session/cache/display.rs @@ -174,6 +174,7 @@ pub fn format_cache_status_inline(cache_status: &CacheStatus) -> Option { } } FingerprintMismatch::InputConfig => "input configuration changed", + FingerprintMismatch::OutputConfig => "output configuration changed", FingerprintMismatch::InputChanged { kind, path } => { let desc = format_input_change_str(*kind, path.as_str()); return Some(vite_str::format!("○ cache miss: {desc}, executing")); diff --git a/crates/vite_task/src/session/cache/mod.rs b/crates/vite_task/src/session/cache/mod.rs index 885dc97d..e37c7ecf 100644 --- a/crates/vite_task/src/session/cache/mod.rs +++ b/crates/vite_task/src/session/cache/mod.rs @@ -1,5 +1,6 @@ //! Execution cache for storing and retrieving cached command results. +pub mod archive; pub mod display; use std::{collections::BTreeMap, fmt::Display, fs::File, io::Write, sync::Arc, time::Duration}; @@ -14,7 +15,8 @@ use rusqlite::{Connection, OptionalExtension as _, config::DbConfig}; use serde::{Deserialize, Serialize}; use tokio::sync::Mutex; use vite_path::{AbsolutePath, RelativePathBuf}; -use vite_task_graph::config::ResolvedInputConfig; +use vite_str::Str; +use vite_task_graph::config::ResolvedGlobConfig; use vite_task_plan::cache_metadata::{CacheMetadata, ExecutionCacheKey, SpawnFingerprint}; use wincode::{ SchemaRead, SchemaReadOwned, SchemaWrite, @@ -43,7 +45,10 @@ pub struct CacheEntryKey { pub spawn_fingerprint: SpawnFingerprint, /// Resolved input configuration that affects cache behavior. /// Glob patterns are workspace-root-relative. - pub input_config: ResolvedInputConfig, + pub input_config: ResolvedGlobConfig, + /// Resolved output configuration that affects cache restoration. + /// Glob patterns are workspace-root-relative. + pub output_config: ResolvedGlobConfig, } impl CacheEntryKey { @@ -51,6 +56,7 @@ impl CacheEntryKey { Self { spawn_fingerprint: cache_metadata.spawn_fingerprint.clone(), input_config: cache_metadata.input_config.clone(), + output_config: cache_metadata.output_config.clone(), } } } @@ -103,6 +109,9 @@ pub struct CacheEntryValue { /// Path is relative to workspace root, value is `xxHash3_64` of file content. /// Stored in the value (not the key) so changes can be detected and reported. pub globbed_inputs: BTreeMap, + /// Filename of the output archive (e.g. `{uuid}.tar.zst`) stored alongside + /// `cache.db` in the cache directory. `None` if no output files were produced. + pub output_archive: Option, } #[derive(Debug)] @@ -142,6 +151,8 @@ pub enum FingerprintMismatch { }, /// Found a previous cache entry key for the same task, but `input_config` differs. InputConfig, + /// Found a previous cache entry key for the same task, but `output_config` differs. + OutputConfig, InputChanged { kind: InputChangeKind, @@ -158,6 +169,9 @@ impl Display for FingerprintMismatch { Self::InputConfig => { write!(f, "input configuration changed") } + Self::OutputConfig => { + write!(f, "output configuration changed") + } Self::InputChanged { kind, path } => { write!(f, "{}", display::format_input_change_str(*kind, path.as_str())) } @@ -201,16 +215,16 @@ impl ExecutionCache { "CREATE TABLE task_fingerprints (key BLOB PRIMARY KEY, value BLOB);", (), )?; - conn.execute("PRAGMA user_version = 11", ())?; + conn.execute("PRAGMA user_version = 12", ())?; } - 1..=10 => { + 1..=11 => { // old internal db version. reset conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, true)?; conn.execute("VACUUM", ())?; conn.set_db_config(DbConfig::SQLITE_DBCONFIG_RESET_DATABASE, false)?; } - 11 => break, // current version - 12.. => { + 12 => break, // current version + 13.. => { return Err(anyhow::anyhow!("Unrecognized database version: {user_version}")); } } @@ -266,11 +280,20 @@ impl ExecutionCache { self.get_cache_key_by_execution_key(execution_cache_key).await? { // Destructure to ensure we handle all fields when new ones are added - let CacheEntryKey { spawn_fingerprint: old_spawn_fingerprint, input_config: _ } = - old_cache_key; + let CacheEntryKey { + spawn_fingerprint: old_spawn_fingerprint, + input_config: old_input_config, + output_config: old_output_config, + } = old_cache_key; let mismatch = if old_spawn_fingerprint == *spawn_fingerprint { - // spawn fingerprint is the same but input_config or glob_base changed - FingerprintMismatch::InputConfig + // spawn fingerprint is the same but input_config or output_config changed + if old_input_config != cache_metadata.input_config { + FingerprintMismatch::InputConfig + } else if old_output_config != cache_metadata.output_config { + FingerprintMismatch::OutputConfig + } else { + FingerprintMismatch::InputConfig + } } else { FingerprintMismatch::SpawnFingerprint { old: old_spawn_fingerprint, @@ -284,16 +307,33 @@ impl ExecutionCache { } /// Update cache after successful execution. + /// + /// If a previous entry exists for the same cache key with a different + /// `output_archive`, the stale archive file in `cache_dir` is removed + /// (best-effort) so it doesn't accumulate on disk. #[tracing::instrument(level = "debug", skip_all)] pub async fn update( &self, cache_metadata: &CacheMetadata, cache_value: CacheEntryValue, + cache_dir: &AbsolutePath, ) -> anyhow::Result<()> { let execution_cache_key = &cache_metadata.execution_cache_key; let cache_key = CacheEntryKey::from_metadata(cache_metadata); + // If a previous entry exists with a stale output archive, delete the + // old file so the cache directory doesn't accumulate orphaned archives. + if let Some(old_value) = self.get_by_cache_key(&cache_key).await? + && let Some(old_archive) = old_value.output_archive + && cache_value.output_archive.as_ref() != Some(&old_archive) + { + let old_archive_path = cache_dir.join(old_archive.as_str()); + // Best-effort cleanup: a missing file (e.g. after a crash or manual + // cache clear) is fine, so we ignore the error. + let _ = std::fs::remove_file(old_archive_path.as_path()); + } + self.upsert_cache_entry(&cache_key, &cache_value).await?; self.upsert_task_fingerprint(execution_cache_key, &cache_key).await?; Ok(()) diff --git a/crates/vite_task/src/session/execute/glob_inputs.rs b/crates/vite_task/src/session/execute/glob_inputs.rs index a9bb1b20..a3d966e1 100644 --- a/crates/vite_task/src/session/execute/glob_inputs.rs +++ b/crates/vite_task/src/session/execute/glob_inputs.rs @@ -109,6 +109,58 @@ pub fn compute_globbed_inputs( Ok(result) } +/// Collect file paths matching positive globs, filtered by negative globs. +/// +/// Like [`compute_globbed_inputs`] but only collects paths (no hashing). +/// Used for determining which output files to archive. +pub fn collect_glob_paths( + workspace_root: &AbsolutePath, + positive_globs: &std::collections::BTreeSet, + negative_globs: &std::collections::BTreeSet, +) -> anyhow::Result> { + if positive_globs.is_empty() { + return Ok(Vec::new()); + } + + let negatives: Vec> = negative_globs + .iter() + .map(|p| Ok(Glob::new(p.as_str())?.into_owned())) + .collect::>()?; + let negation = wax::any(negatives)?; + + let mut result = Vec::new(); + + for pattern in positive_globs { + let glob = Glob::new(pattern.as_str())?.into_owned(); + let walk = glob.walk(workspace_root.as_path()); + for entry in walk.not(negation.clone())? { + let entry = match entry { + Ok(entry) => entry, + Err(err) => { + let io_err: io::Error = err.into(); + if io_err.kind() == io::ErrorKind::NotFound { + continue; + } + return Err(io_err.into()); + } + }; + if !entry.file_type().is_file() { + continue; + } + let path = entry.path(); + let Some(stripped) = path.strip_prefix(workspace_root.as_path()).ok() else { + continue; + }; + let relative = RelativePathBuf::new(stripped)?; + result.push(relative); + } + } + + result.sort(); + result.dedup(); + Ok(result) +} + #[expect(clippy::disallowed_types, reason = "receives std::path::Path from wax glob walker")] fn hash_file_content(path: &std::path::Path) -> io::Result { super::hash::hash_content(io::BufReader::new(File::open(path)?)) diff --git a/crates/vite_task/src/session/execute/mod.rs b/crates/vite_task/src/session/execute/mod.rs index 8af09888..95ee16a5 100644 --- a/crates/vite_task/src/session/execute/mod.rs +++ b/crates/vite_task/src/session/execute/mod.rs @@ -11,14 +11,16 @@ use std::{cell::RefCell, collections::BTreeMap, io::Write as _, sync::Arc, time: use futures_util::{FutureExt, StreamExt, future::LocalBoxFuture, stream::FuturesUnordered}; use petgraph::Direction; -use rustc_hash::FxHashMap; +use rustc_hash::{FxHashMap, FxHashSet}; use tokio::sync::Semaphore; use tokio_util::sync::CancellationToken; use vite_path::{AbsolutePath, RelativePathBuf}; +use vite_str::Str; use vite_task_plan::{ ExecutionGraph, ExecutionItemDisplay, ExecutionItemKind, LeafExecutionKind, SpawnExecution, cache_metadata::CacheMetadata, execution_graph::ExecutionNodeIndex, }; +use wax::Program as _; use self::{ fingerprint::PostRunFingerprint, @@ -28,7 +30,7 @@ use self::{ tracked_accesses::TrackedPathAccesses, }; use super::{ - cache::{CacheEntryValue, ExecutionCache}, + cache::{CacheEntryValue, ExecutionCache, archive}, event::{ CacheDisabledReason, CacheErrorKind, CacheNotUpdatedReason, CacheStatus, CacheUpdateStatus, ExecutionError, @@ -73,6 +75,8 @@ struct ExecutionContext<'a> { /// Base path for resolving relative paths in cache entries. /// Typically the workspace root. cache_base_path: &'a Arc, + /// Directory where cache files (db, archives) are stored. + cache_dir: &'a AbsolutePath, /// Token cancelled when a task fails. Kills in-flight child processes /// (via `start_kill` in spawn.rs), prevents scheduling new tasks, and /// prevents caching results of concurrently-running tasks. @@ -234,6 +238,7 @@ impl ExecutionContext<'_> { spawn_execution, self.cache, self.cache_base_path, + self.cache_dir, self.fast_fail_token.clone(), self.interrupt_token.clone(), ) @@ -285,10 +290,11 @@ struct CacheState<'a> { /// Captured stdout/stderr for cache replay. Written in place during drain; /// always present (possibly empty) once we reach the cache-update phase. std_outputs: Vec, - /// `Some` iff fspy is enabled (`includes_auto`). Holds the resolved - /// negative globs used by [`TrackedPathAccesses::from_raw`] to filter - /// tracked accesses. `None` means fspy tracking is off for this task. - fspy_negatives: Option>>, + /// Fspy tracking status and pre-resolved input negative globs. + /// `None` means fspy tracking is off for this task. `Some(globs)` means + /// fspy is on; the globs are used to filter inferred input reads (not + /// writes — output negatives are applied separately during archiving). + fspy_input_negatives: Option>>, } /// Execute a spawned process with cache-aware lifecycle. @@ -315,6 +321,7 @@ pub async fn execute_spawn( spawn_execution: &SpawnExecution, cache: &ExecutionCache, cache_base_path: &Arc, + cache_dir: &AbsolutePath, fast_fail_token: CancellationToken, interrupt_token: CancellationToken, ) -> SpawnOutcome { @@ -387,6 +394,18 @@ pub async fn execute_spawn( let _ = writer.write_all(&output.content); let _ = writer.flush(); } + // Restore output files from the cached archive + if let Some(ref archive_name) = cached.output_archive { + let archive_path = cache_dir.join(archive_name.as_str()); + if let Err(err) = archive::extract_output_archive(cache_base_path, &archive_path) { + leaf_reporter.finish( + None, + CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheHit), + Some(ExecutionError::Cache { kind: CacheErrorKind::Lookup, source: err }), + ); + return SpawnOutcome::Failed; + } + } leaf_reporter.finish( None, CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheHit), @@ -411,36 +430,38 @@ pub async fn execute_spawn( // ───────────────────────────────────────────────────────────────────── let mut mode: ExecutionMode<'_> = match cache_metadata { Some(metadata) => { - let fspy = if metadata.input_config.includes_auto { - // Resolve negative globs for fspy path filtering - // (already workspace-root-relative). - match metadata - .input_config - .negative_globs - .iter() - .map(|p| Ok(wax::Glob::new(p.as_str())?.into_owned())) - .collect::>>() - { - Ok(negs) => Some(negs), - Err(err) => { - leaf_reporter.finish( - None, - CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), - Some(ExecutionError::PostRunFingerprint(err)), - ); - return SpawnOutcome::Failed; + let fspy = + if metadata.input_config.includes_auto || metadata.output_config.includes_auto { + // Resolve input negative globs for fspy path filtering + // (already workspace-root-relative). Output negatives are applied + // later in `collect_and_archive_outputs`. + match metadata + .input_config + .negative_globs + .iter() + .map(|p| Ok(wax::Glob::new(p.as_str())?.into_owned())) + .collect::>>() + { + Ok(negs) => Some(negs), + Err(err) => { + leaf_reporter.finish( + None, + CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), + Some(ExecutionError::PostRunFingerprint(err)), + ); + return SpawnOutcome::Failed; + } } - } - } else { - None - }; + } else { + None + }; ExecutionMode::Cached { pipe_writers: stdio_config.writers, state: CacheState { metadata, globbed_inputs, std_outputs: Vec::new(), - fspy_negatives: fspy, + fspy_input_negatives: fspy, }, } } @@ -452,7 +473,9 @@ pub async fn execute_spawn( // 5. Derive the arguments for `spawn()` from the mode without consuming it. let (spawn_stdio, fspy_enabled) = match &mode { - ExecutionMode::Cached { state, .. } => (SpawnStdio::Piped, state.fspy_negatives.is_some()), + ExecutionMode::Cached { state, .. } => { + (SpawnStdio::Piped, state.fspy_input_negatives.is_some()) + } ExecutionMode::Uncached { pipe_writers: Some(_) } => (SpawnStdio::Piped, false), ExecutionMode::Uncached { pipe_writers: None } => (SpawnStdio::Inherited, false), }; @@ -536,79 +559,124 @@ pub async fn execute_spawn( // 9. Cache update (only when we were in `Cached` mode). Errors during cache // update are reported but do not affect the exit status we return. - let (cache_update_status, cache_error) = if let ExecutionMode::Cached { state, .. } = mode { - let CacheState { metadata, globbed_inputs, std_outputs, fspy_negatives } = state; - - // Normalize fspy accesses. `zip` gives `Some` iff fspy was enabled - // (both outcome.path_accesses and fspy_negatives are Some together). - let path_accesses = outcome - .path_accesses - .as_ref() - .zip(fspy_negatives.as_deref()) - .map(|(raw, negs)| TrackedPathAccesses::from_raw(raw, cache_base_path, negs)); - - let cancelled = fast_fail_token.is_cancelled() || interrupt_token.is_cancelled(); - if cancelled { - // Cancelled (Ctrl-C or sibling failure) — result is untrustworthy - (CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::Cancelled), None) - } else if outcome.exit_status.success() { - // Check for read-write overlap: if the task wrote to any file it also - // read, the inputs were modified during execution — don't cache. - // Note: this only checks fspy-inferred reads, not globbed_inputs keys. - // A task that writes to a glob-matched file without reading it causes - // perpetual cache misses (glob detects the hash change) but not a - // correctness bug, so we don't handle that case here. - if let Some(path) = path_accesses + let (cache_update_status, cache_error) = 'cache_update: { + if let ExecutionMode::Cached { state, .. } = mode { + let CacheState { metadata, globbed_inputs, std_outputs, fspy_input_negatives } = state; + + // Normalize fspy accesses. `Some` iff fspy was enabled at spawn time. + // User-configured negatives are applied below, separately for reads + // (input negatives) and writes (output negatives, inside + // `collect_and_archive_outputs`). + let path_accesses = outcome + .path_accesses .as_ref() - .and_then(|pa| pa.path_reads.keys().find(|p| pa.path_writes.contains(*p))) - { - ( - CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::InputModified { - path: path.clone(), - }), - None, - ) - } else { - // path_reads is empty when inference is disabled (path_accesses is None) - let empty_path_reads = HashMap::default(); - let path_reads = - path_accesses.as_ref().map_or(&empty_path_reads, |pa| &pa.path_reads); - - // Execution succeeded — attempt to create fingerprint and update cache. - // Paths already in globbed_inputs are skipped: Rule 1 (above) guarantees - // no input modification, so the prerun hash is the correct post-exec hash. - match PostRunFingerprint::create(path_reads, cache_base_path, &globbed_inputs) { - Ok(post_run_fingerprint) => { - let new_cache_value = CacheEntryValue { - post_run_fingerprint, - std_outputs: std_outputs.into(), - duration, - globbed_inputs, - }; - match cache.update(metadata, new_cache_value).await { - Ok(()) => (CacheUpdateStatus::Updated, None), - Err(err) => ( - CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), - Some(ExecutionError::Cache { - kind: CacheErrorKind::Update, - source: err, - }), - ), + .map(|raw| TrackedPathAccesses::from_raw(raw, cache_base_path)); + + // Inferred input reads: gated by `input_config.includes_auto` and + // filtered by input negatives. When input auto is disabled, no reads + // contribute to the fingerprint even if fspy was enabled for output + // tracking. + let empty_reads = HashMap::default(); + let inferred_reads: HashMap = + if metadata.input_config.includes_auto + && let Some(pa) = path_accesses.as_ref() + && let Some(negatives) = fspy_input_negatives.as_deref() + { + pa.path_reads + .iter() + .filter(|(path, _)| { + !negatives.iter().any(|neg| wax::Program::is_match(neg, path.as_str())) + }) + .map(|(path, read)| (path.clone(), *read)) + .collect() + } else { + empty_reads + }; + + let cancelled = fast_fail_token.is_cancelled() || interrupt_token.is_cancelled(); + if cancelled { + // Cancelled (Ctrl-C or sibling failure) — result is untrustworthy + (CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::Cancelled), None) + } else if outcome.exit_status.success() { + // Check for read-write overlap: if the task wrote to any file it also + // read (as an inferred input), the inputs were modified during + // execution — don't cache. Reads excluded by input negatives (or + // when input auto is off) don't count. + if let Some(path) = path_accesses + .as_ref() + .and_then(|pa| inferred_reads.keys().find(|p| pa.path_writes.contains(*p))) + { + ( + CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::InputModified { + path: path.clone(), + }), + None, + ) + } else { + // Execution succeeded — attempt to create fingerprint and update cache. + // Paths already in globbed_inputs are skipped: Rule 1 (above) guarantees + // no input modification, so the prerun hash is the correct post-exec hash. + match PostRunFingerprint::create( + &inferred_reads, + cache_base_path, + &globbed_inputs, + ) { + Ok(post_run_fingerprint) => { + // Collect output files and create archive + let output_archive = match collect_and_archive_outputs( + metadata, + path_accesses.as_ref(), + cache_base_path, + cache_dir, + ) { + Ok(archive) => archive, + Err(err) => { + break 'cache_update ( + CacheUpdateStatus::NotUpdated( + CacheNotUpdatedReason::CacheDisabled, + ), + Some(ExecutionError::Cache { + kind: CacheErrorKind::Update, + source: err, + }), + ); + } + }; + + let new_cache_value = CacheEntryValue { + post_run_fingerprint, + std_outputs: std_outputs.into(), + duration, + globbed_inputs, + output_archive, + }; + match cache.update(metadata, new_cache_value, cache_dir).await { + Ok(()) => (CacheUpdateStatus::Updated, None), + Err(err) => ( + CacheUpdateStatus::NotUpdated( + CacheNotUpdatedReason::CacheDisabled, + ), + Some(ExecutionError::Cache { + kind: CacheErrorKind::Update, + source: err, + }), + ), + } } + Err(err) => ( + CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), + Some(ExecutionError::PostRunFingerprint(err)), + ), } - Err(err) => ( - CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), - Some(ExecutionError::PostRunFingerprint(err)), - ), } + } else { + // Execution failed with non-zero exit status — don't update cache + (CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::NonZeroExitStatus), None) } } else { - // Execution failed with non-zero exit status — don't update cache - (CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::NonZeroExitStatus), None) + // Caching was disabled for this task + (CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), None) } - } else { - // Caching was disabled for this task - (CacheUpdateStatus::NotUpdated(CacheNotUpdatedReason::CacheDisabled), None) }; // 7. Finish the leaf execution with the result and optional cache error. @@ -619,6 +687,68 @@ pub async fn execute_spawn( SpawnOutcome::Spawned(outcome.exit_status) } +/// Collect output files and create a tar.zst archive in the cache directory. +/// +/// Output files are determined by: +/// - fspy-tracked writes (when `output_config.includes_auto` is true) +/// - Positive output globs (always, if configured) +/// - Filtered by negative output globs +/// +/// Returns `Some(archive_filename)` if files were archived, `None` if no output files. +fn collect_and_archive_outputs( + cache_metadata: &vite_task_plan::cache_metadata::CacheMetadata, + path_accesses: Option<&TrackedPathAccesses>, + workspace_root: &AbsolutePath, + cache_dir: &AbsolutePath, +) -> anyhow::Result> { + let output_config = &cache_metadata.output_config; + + // Collect output files from auto-detection (fspy writes) + let mut output_files: FxHashSet = FxHashSet::default(); + + if output_config.includes_auto + && let Some(pa) = path_accesses + { + output_files.extend(pa.path_writes.iter().cloned()); + } + + // Collect output files from positive globs + if !output_config.positive_globs.is_empty() { + let glob_paths = glob_inputs::collect_glob_paths( + workspace_root, + &output_config.positive_globs, + &output_config.negative_globs, + )?; + output_files.extend(glob_paths); + } + + // Apply negative globs to auto-detected files + if output_config.includes_auto && !output_config.negative_globs.is_empty() { + let negatives: Vec> = output_config + .negative_globs + .iter() + .map(|p| Ok(wax::Glob::new(p.as_str())?.into_owned())) + .collect::>()?; + output_files.retain(|path| !negatives.iter().any(|neg| neg.is_match(path.as_str()))); + } + + if output_files.is_empty() { + return Ok(None); + } + + // Sort for deterministic archive content + let mut sorted_files: Vec = output_files.into_iter().collect(); + sorted_files.sort(); + + // Create archive with UUID filename + let archive_name: Str = vite_str::format!("{}.tar.zst", uuid::Uuid::new_v4()); + let archive_path = cache_dir.join(archive_name.as_str()); + + archive::create_output_archive(workspace_root, &sorted_files, &archive_path)?; + + Ok(Some(archive_name)) +} + impl Session<'_> { /// Execute an execution graph, reporting events through the provided reporter builder. /// @@ -653,6 +783,7 @@ impl Session<'_> { reporter: &reporter, cache, cache_base_path: &self.workspace_path, + cache_dir: &self.cache_path, fast_fail_token: CancellationToken::new(), interrupt_token, }; diff --git a/crates/vite_task/src/session/execute/tracked_accesses.rs b/crates/vite_task/src/session/execute/tracked_accesses.rs index 79c77f10..0d06968c 100644 --- a/crates/vite_task/src/session/execute/tracked_accesses.rs +++ b/crates/vite_task/src/session/execute/tracked_accesses.rs @@ -8,6 +8,10 @@ use vite_path::{AbsolutePath, RelativePathBuf}; use crate::collections::HashMap; +/// User-configured negative globs are NOT applied here. They are applied later, +/// separately for reads (input config) and writes (output config), since those +/// two configs are independent. + /// Path read access info #[derive(Debug, Clone, Copy)] pub struct PathRead { @@ -25,22 +29,19 @@ pub struct TrackedPathAccesses { } impl TrackedPathAccesses { - /// Build from fspy's raw iterable by stripping the workspace prefix, - /// normalizing `..` components, and filtering against the negative globs. - pub fn from_raw( - raw: &PathAccessIterable, - workspace_root: &AbsolutePath, - resolved_negatives: &[wax::Glob<'static>], - ) -> Self { + /// Build from fspy's raw iterable by stripping the workspace prefix and + /// normalizing `..` components. `.git/*` paths are skipped. User-configured + /// negatives are applied by the caller (see module docs). + pub fn from_raw(raw: &PathAccessIterable, workspace_root: &AbsolutePath) -> Self { let mut accesses = Self::default(); for access in raw.iter() { - // Strip workspace root, clean `..` components, and filter in one pass. + // Strip workspace root and clean `..` components in one pass. // fspy may report paths like `packages/sub-pkg/../shared/dist/output.js`. let relative_path = access.path.strip_path_prefix(workspace_root, |strip_result| { let Ok(stripped_path) = strip_result else { return None; }; - normalize_tracked_workspace_path(stripped_path, resolved_negatives) + normalize_tracked_workspace_path(stripped_path) }); let Some(relative_path) = relative_path else { @@ -75,10 +76,7 @@ impl TrackedPathAccesses { clippy::disallowed_types, reason = "fspy strip_path_prefix exposes std::path::Path; convert to RelativePathBuf immediately" )] -fn normalize_tracked_workspace_path( - stripped_path: &std::path::Path, - resolved_negatives: &[wax::Glob<'static>], -) -> Option { +fn normalize_tracked_workspace_path(stripped_path: &std::path::Path) -> Option { // On Windows, paths are possible to be still absolute after stripping the workspace root. // For example: c:\workspace\subdir\c:\workspace\subdir // Just ignore those accesses. @@ -94,12 +92,6 @@ fn normalize_tracked_workspace_path( return None; } - if !resolved_negatives.is_empty() - && resolved_negatives.iter().any(|neg| wax::Program::is_match(neg, relative.as_str())) - { - return None; - } - Some(relative) } @@ -115,8 +107,7 @@ mod tests { clippy::disallowed_types, reason = "normalize_tracked_workspace_path requires std::path::Path for fspy strip_path_prefix output" )] - let relative_path = - normalize_tracked_workspace_path(std::path::Path::new(r"foo\C:\bar"), &[]); + let relative_path = normalize_tracked_workspace_path(std::path::Path::new(r"foo\C:\bar")); assert!(relative_path.is_none()); } } diff --git a/crates/vite_task/src/session/mod.rs b/crates/vite_task/src/session/mod.rs index a6968c90..8b97fb6b 100644 --- a/crates/vite_task/src/session/mod.rs +++ b/crates/vite_task/src/session/mod.rs @@ -675,6 +675,7 @@ impl<'a> Session<'a> { &spawn_execution, cache, &self.workspace_path, + &self.cache_path, tokio_util::sync::CancellationToken::new(), tokio_util::sync::CancellationToken::new(), ) diff --git a/crates/vite_task/src/session/reporter/summary.rs b/crates/vite_task/src/session/reporter/summary.rs index 81c314b1..3f4124a3 100644 --- a/crates/vite_task/src/session/reporter/summary.rs +++ b/crates/vite_task/src/session/reporter/summary.rs @@ -254,7 +254,9 @@ impl SavedCacheMissReason { FingerprintMismatch::SpawnFingerprint { old, new } => { Self::SpawnFingerprintChanged(detect_spawn_fingerprint_changes(old, new)) } - FingerprintMismatch::InputConfig => Self::ConfigChanged, + FingerprintMismatch::InputConfig | FingerprintMismatch::OutputConfig => { + Self::ConfigChanged + } FingerprintMismatch::InputChanged { kind, path } => { Self::InputChanged { kind: *kind, path: Str::from(path.as_str()) } } diff --git a/crates/vite_task_bin/src/lib.rs b/crates/vite_task_bin/src/lib.rs index 32dc72f3..e4211000 100644 --- a/crates/vite_task_bin/src/lib.rs +++ b/crates/vite_task_bin/src/lib.rs @@ -98,6 +98,7 @@ impl vite_task::CommandHandler for CommandHandler { env: None, untracked_env: None, input: None, + output: None, }), envs: Arc::clone(&command.envs), })) diff --git a/crates/vite_task_bin/src/vtt/write_file.rs b/crates/vite_task_bin/src/vtt/write_file.rs index f5539146..b1562bd7 100644 --- a/crates/vite_task_bin/src/vtt/write_file.rs +++ b/crates/vite_task_bin/src/vtt/write_file.rs @@ -2,6 +2,10 @@ pub fn run(args: &[String]) -> Result<(), Box> { if args.len() < 2 { return Err("Usage: vtt write-file ".into()); } - std::fs::write(&args[0], &args[1])?; + let path = std::path::Path::new(&args[0]); + if let Some(parent) = path.parent() { + std::fs::create_dir_all(parent)?; + } + std::fs::write(path, &args[1])?; Ok(()) } diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/input_cache_test/snapshots/fspy_env___not_set_when_auto_inference_disabled.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/input_cache_test/snapshots/fspy_env___not_set_when_auto_inference_disabled.md index 2cf8e6d7..d9fb1a15 100644 --- a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/input_cache_test/snapshots/fspy_env___not_set_when_auto_inference_disabled.md +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/input_cache_test/snapshots/fspy_env___not_set_when_auto_inference_disabled.md @@ -6,5 +6,5 @@ Test all input configuration combinations for cache behavior ``` $ vtt print-env FSPY -(undefined) +1 ``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/README.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/README.md new file mode 100644 index 00000000..626799f0 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/README.md @@ -0,0 +1 @@ +v1 diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/package.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/package.json new file mode 100644 index 00000000..60b87e87 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/package.json @@ -0,0 +1,4 @@ +{ + "name": "output-cache-test", + "private": true +} diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots.toml b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots.toml new file mode 100644 index 00000000..eb70ab9c --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots.toml @@ -0,0 +1,157 @@ +# Test output restoration when cached tasks are replayed + +# 1. Auto output detection (default): output files written by the task are restored on cache hit +[[e2e]] +name = "auto_output___files_restored_on_cache_hit" +steps = [ + # First run - writes dist/out.txt + ["vt", "run", "auto-output"], + # Verify output exists + ["vtt", "print-file", "dist/out.txt"], + # Delete the output file (keep directory to avoid fspy inferred-input miss on Windows) + ["vtt", "rm", "dist/out.txt"], + # Second run - cache hit, should restore dist/out.txt + ["vt", "run", "auto-output"], + # Verify output was restored + ["vtt", "print-file", "dist/out.txt"], +] + +# 2. Glob output: only files matching output globs are restored +[[e2e]] +name = "glob_output___only_matched_files_restored" +steps = [ + [ + "vt", + "run", + "glob-output", + ], + [ + "vtt", + "rm", + "dist/out.txt", + ], + [ + "vtt", + "rm", + "tmp/temp.txt", + ], + # Second run - cache hit, should restore dist/out.txt but not tmp/temp.txt + [ + "vt", + "run", + "glob-output", + ], + [ + "vtt", + "print-file", + "dist/out.txt", + ], + # tmp/temp.txt should NOT exist (not in output globs) + { argv = [ + "vtt", + "print-file", + "tmp/temp.txt", + ], comment = "should fail - tmp not in output globs" }, +] + +# 3. Auto output works independently of input auto +[[e2e]] +name = "auto_output_with_non_auto_input" +steps = [ + # First run - input: ["src/**"] (no auto), output: default (auto) + ["vt", "run", "auto-output-no-auto-input"], + ["vtt", "rm", "dist/out.txt"], + # Cache hit - output files should still be restored + ["vt", "run", "auto-output-no-auto-input"], + ["vtt", "print-file", "dist/out.txt"], +] + +# 4. Negative output globs exclude files from restoration +[[e2e]] +name = "negative_output___excluded_files_not_restored" +steps = [ + [ + "vt", + "run", + "negative-output", + ], + [ + "vtt", + "rm", + "dist/out.txt", + ], + [ + "vtt", + "rm", + "dist/cache/tmp.txt", + ], + # Cache hit - should restore dist/out.txt but NOT dist/cache/tmp.txt + [ + "vt", + "run", + "negative-output", + ], + [ + "vtt", + "print-file", + "dist/out.txt", + ], + # dist/cache/tmp.txt should NOT exist (excluded by !dist/cache/**) + { argv = [ + "vtt", + "print-file", + "dist/cache/tmp.txt", + ], comment = "should fail - excluded by negative glob" }, +] + +# 5. Changing output config invalidates cache +[[e2e]] +name = "output_config_change_invalidates_cache" +steps = [ + ["vt", "run", "output-config-change"], + # Change output config + ["vtt", "replace-file-content", "vite-task.json", 'REPLACE_ME', 'dist/**'], + # Should be a cache miss due to output config change + { argv = ["vt", "run", "output-config-change"], comment = "cache miss: output config changed" }, +] + +# 6. Input negative globs must not drop matching writes from the output archive. +# Here the user excludes `dist/**` from inferred inputs (so rewriting dist/ won't +# mark inputs as modified), but default auto output should still capture dist +# writes and restore them on a cache hit. +[[e2e]] +name = "input_negative_does_not_drop_output_writes" +steps = [ + ["vt", "run", "input-neg-dist-auto-output"], + ["vtt", "rm", "dist/out.txt"], + # Cache hit should restore dist/out.txt + ["vt", "run", "input-neg-dist-auto-output"], + ["vtt", "print-file", "dist/out.txt"], +] + +# 7. When input auto is disabled (explicit globs only), unrelated reads tracked +# by fspy must NOT become inferred inputs. Default auto output still needs fspy +# for write tracking, but reads outside `input: ["src/**"]` should be ignored. +[[e2e]] +name = "explicit_input_ignores_fspy_reads" +steps = [ + [ + "vt", + "run", + "explicit-input-auto-output", + ], + # Modify README.md (read by the task, NOT in input globs) + [ + "vtt", + "replace-file-content", + "README.md", + "v1", + "v2", + ], + # Should be a cache hit: README.md is not an input + { argv = [ + "vt", + "run", + "explicit-input-auto-output", + ], comment = "cache hit: README.md not in input globs" }, +] diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/auto_output___files_restored_on_cache_hit.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/auto_output___files_restored_on_cache_hit.md new file mode 100644 index 00000000..b0b9c160 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/auto_output___files_restored_on_cache_hit.md @@ -0,0 +1,33 @@ +# auto_output___files_restored_on_cache_hit + +## `vt run auto-output` + +``` +$ vtt write-file dist/out.txt built +``` + +## `vtt print-file dist/out.txt` + +``` +built +``` + +## `vtt rm dist/out.txt` + +``` +``` + +## `vt run auto-output` + +``` +$ vtt write-file dist/out.txt built ◉ cache hit, replaying + +--- +vt run: cache hit. +``` + +## `vtt print-file dist/out.txt` + +``` +built +``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/auto_output_with_non_auto_input.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/auto_output_with_non_auto_input.md new file mode 100644 index 00000000..2bac43db --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/auto_output_with_non_auto_input.md @@ -0,0 +1,27 @@ +# auto_output_with_non_auto_input + +## `vt run auto-output-no-auto-input` + +``` +$ vtt write-file dist/out.txt built +``` + +## `vtt rm dist/out.txt` + +``` +``` + +## `vt run auto-output-no-auto-input` + +``` +$ vtt write-file dist/out.txt built ◉ cache hit, replaying + +--- +vt run: cache hit. +``` + +## `vtt print-file dist/out.txt` + +``` +built +``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/explicit_input_ignores_fspy_reads.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/explicit_input_ignores_fspy_reads.md new file mode 100644 index 00000000..91ef3bcc --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/explicit_input_ignores_fspy_reads.md @@ -0,0 +1,25 @@ +# explicit_input_ignores_fspy_reads + +## `vt run explicit-input-auto-output` + +``` +$ vtt print-file README.md +v1 +``` + +## `vtt replace-file-content README.md v1 v2` + +``` +``` + +## `vt run explicit-input-auto-output` + +cache hit: README.md not in input globs + +``` +$ vtt print-file README.md ◉ cache hit, replaying +v1 + +--- +vt run: cache hit. +``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/glob_output___only_matched_files_restored.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/glob_output___only_matched_files_restored.md new file mode 100644 index 00000000..72bead9b --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/glob_output___only_matched_files_restored.md @@ -0,0 +1,47 @@ +# glob_output___only_matched_files_restored + +## `vt run glob-output` + +``` +$ vtt write-file dist/out.txt built + +$ vtt write-file tmp/temp.txt temp + +--- +vt run: 0/2 cache hit (0%). (Run `vt run --last-details` for full details) +``` + +## `vtt rm dist/out.txt` + +``` +``` + +## `vtt rm tmp/temp.txt` + +``` +``` + +## `vt run glob-output` + +``` +$ vtt write-file dist/out.txt built ◉ cache hit, replaying + +$ vtt write-file tmp/temp.txt temp ◉ cache hit, replaying + +--- +vt run: 2/2 cache hit (100%). (Run `vt run --last-details` for full details) +``` + +## `vtt print-file dist/out.txt` + +``` +built +``` + +## `vtt print-file tmp/temp.txt` + +should fail - tmp not in output globs + +``` +tmp/temp.txt: not found +``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/input_negative_does_not_drop_output_writes.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/input_negative_does_not_drop_output_writes.md new file mode 100644 index 00000000..ca5a8f22 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/input_negative_does_not_drop_output_writes.md @@ -0,0 +1,27 @@ +# input_negative_does_not_drop_output_writes + +## `vt run input-neg-dist-auto-output` + +``` +$ vtt write-file dist/out.txt built +``` + +## `vtt rm dist/out.txt` + +``` +``` + +## `vt run input-neg-dist-auto-output` + +``` +$ vtt write-file dist/out.txt built ◉ cache hit, replaying + +--- +vt run: cache hit. +``` + +## `vtt print-file dist/out.txt` + +``` +built +``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/negative_output___excluded_files_not_restored.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/negative_output___excluded_files_not_restored.md new file mode 100644 index 00000000..23268e69 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/negative_output___excluded_files_not_restored.md @@ -0,0 +1,47 @@ +# negative_output___excluded_files_not_restored + +## `vt run negative-output` + +``` +$ vtt write-file dist/out.txt built + +$ vtt write-file dist/cache/tmp.txt temp + +--- +vt run: 0/2 cache hit (0%). (Run `vt run --last-details` for full details) +``` + +## `vtt rm dist/out.txt` + +``` +``` + +## `vtt rm dist/cache/tmp.txt` + +``` +``` + +## `vt run negative-output` + +``` +$ vtt write-file dist/out.txt built ◉ cache hit, replaying + +$ vtt write-file dist/cache/tmp.txt temp ◉ cache hit, replaying + +--- +vt run: 2/2 cache hit (100%). (Run `vt run --last-details` for full details) +``` + +## `vtt print-file dist/out.txt` + +``` +built +``` + +## `vtt print-file dist/cache/tmp.txt` + +should fail - excluded by negative glob + +``` +dist/cache/tmp.txt: not found +``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/output_config_change_invalidates_cache.md b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/output_config_change_invalidates_cache.md new file mode 100644 index 00000000..bc83ac5c --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/snapshots/output_config_change_invalidates_cache.md @@ -0,0 +1,20 @@ +# output_config_change_invalidates_cache + +## `vt run output-config-change` + +``` +$ vtt write-file dist/out.txt built +``` + +## `vtt replace-file-content vite-task.json REPLACE_ME dist/**` + +``` +``` + +## `vt run output-config-change` + +cache miss: output config changed + +``` +$ vtt write-file dist/out.txt built ○ cache miss: output configuration changed, executing +``` diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/src/main.ts b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/src/main.ts new file mode 100644 index 00000000..38e000a1 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/src/main.ts @@ -0,0 +1 @@ +export const main = 'initial'; diff --git a/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/vite-task.json b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/vite-task.json new file mode 100644 index 00000000..f7b27146 --- /dev/null +++ b/crates/vite_task_bin/tests/e2e_snapshots/fixtures/output_cache_test/vite-task.json @@ -0,0 +1,38 @@ +{ + "tasks": { + "auto-output": { + "command": "vtt write-file dist/out.txt built", + "cache": true + }, + "glob-output": { + "command": "vtt write-file dist/out.txt built && vtt write-file tmp/temp.txt temp", + "output": ["dist/**"], + "cache": true + }, + "auto-output-no-auto-input": { + "command": "vtt write-file dist/out.txt built", + "input": ["src/**"], + "cache": true + }, + "negative-output": { + "command": "vtt write-file dist/out.txt built && vtt write-file dist/cache/tmp.txt temp", + "output": [{ "auto": true }, "!dist/cache/**"], + "cache": true + }, + "output-config-change": { + "command": "vtt write-file dist/out.txt built", + "output": ["REPLACE_ME"], + "cache": true + }, + "input-neg-dist-auto-output": { + "command": "vtt write-file dist/out.txt built", + "input": [{ "auto": true }, "!dist/**"], + "cache": true + }, + "explicit-input-auto-output": { + "command": "vtt print-file README.md", + "input": ["src/**"], + "cache": true + } + } +} diff --git a/crates/vite_task_graph/run-config.ts b/crates/vite_task_graph/run-config.ts index 9c7852a9..5b02bfb5 100644 --- a/crates/vite_task_graph/run-config.ts +++ b/crates/vite_task_graph/run-config.ts @@ -53,7 +53,18 @@ untrackedEnv?: Array, * - `{auto: true}` enables automatic file tracking * - Negative patterns (e.g. `"!dist/**"`) exclude matched files */ -input?: Array, } | { +input?: Array, +/** + * Output files to archive and restore on cache hit. + * + * - Omitted: automatically tracks which files the task writes + * - `[]` (empty): disables output restoration entirely + * - Glob patterns (e.g. `"dist/**"`) select specific output files, relative to the package directory + * - `{pattern: "...", base: "workspace" | "package"}` specifies a glob with an explicit base directory + * - `{auto: true}` enables automatic file tracking + * - Negative patterns (e.g. `"!dist/cache/**"`) exclude matched files + */ +output?: Array, } | { /** * Whether to cache the task */ diff --git a/crates/vite_task_graph/src/config/mod.rs b/crates/vite_task_graph/src/config/mod.rs index 5f202c31..5b13c960 100644 --- a/crates/vite_task_graph/src/config/mod.rs +++ b/crates/vite_task_graph/src/config/mod.rs @@ -69,12 +69,18 @@ impl ResolvedTaskOptions { enabled_cache_config.untracked_env.unwrap_or_default().into_iter().collect(); untracked_env.extend(DEFAULT_UNTRACKED_ENV.iter().copied().map(Str::from)); - let input_config = ResolvedInputConfig::from_user_config( + let input_config = ResolvedGlobConfig::from_user_config( enabled_cache_config.input.as_ref(), dir, workspace_root, )?; + let output_config = ResolvedGlobConfig::from_user_config( + enabled_cache_config.output.as_ref(), + dir, + workspace_root, + )?; + Some(CacheConfig { env_config: EnvConfig { fingerprinted_envs: enabled_cache_config @@ -84,6 +90,7 @@ impl ResolvedTaskOptions { untracked_env, }, input_config, + output_config, }) } }; @@ -92,9 +99,14 @@ impl ResolvedTaskOptions { } #[derive(Debug, Clone, Serialize)] +#[expect( + clippy::struct_field_names, + reason = "env_config, input_config, output_config are distinct config categories, not a naming smell" +)] pub struct CacheConfig { pub env_config: EnvConfig, - pub input_config: ResolvedInputConfig, + pub input_config: ResolvedGlobConfig, + pub output_config: ResolvedGlobConfig, } /// Resolved input configuration for cache fingerprinting. @@ -104,7 +116,7 @@ pub struct CacheConfig { /// - `positive_globs`: Glob patterns for files to include (without `!` prefix) /// - `negative_globs`: Glob patterns for files to exclude (without `!` prefix) #[derive(Debug, Clone, PartialEq, Eq, Serialize, SchemaWrite, SchemaRead)] -pub struct ResolvedInputConfig { +pub struct ResolvedGlobConfig { /// Whether automatic file tracking is enabled pub includes_auto: bool, @@ -117,7 +129,7 @@ pub struct ResolvedInputConfig { pub negative_globs: BTreeSet, } -impl ResolvedInputConfig { +impl ResolvedGlobConfig { /// Default configuration: auto-inference enabled, no explicit patterns #[must_use] pub const fn default_auto() -> Self { @@ -425,7 +437,7 @@ mod tests { #[test] fn test_resolved_input_config_default_auto() { - let config = ResolvedInputConfig::default_auto(); + let config = ResolvedGlobConfig::default_auto(); assert!(config.includes_auto); assert!(config.positive_globs.is_empty()); assert!(config.negative_globs.is_empty()); @@ -435,7 +447,7 @@ mod tests { fn test_resolved_input_config_from_none() { let (pkg, ws) = test_paths(); // None means default to auto-inference - let config = ResolvedInputConfig::from_user_config(None, &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(None, &pkg, &ws).unwrap(); assert!(config.includes_auto); assert!(config.positive_globs.is_empty()); assert!(config.negative_globs.is_empty()); @@ -446,7 +458,7 @@ mod tests { let (pkg, ws) = test_paths(); // Empty array means no inputs, inference disabled let user_inputs = vec![]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert!(!config.includes_auto); assert!(config.positive_globs.is_empty()); assert!(config.negative_globs.is_empty()); @@ -456,7 +468,7 @@ mod tests { fn test_resolved_input_config_auto_only() { let (pkg, ws) = test_paths(); let user_inputs = vec![UserInputEntry::Auto(AutoInput { auto: true })]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert!(config.includes_auto); assert!(config.positive_globs.is_empty()); assert!(config.negative_globs.is_empty()); @@ -466,7 +478,7 @@ mod tests { fn test_resolved_input_config_auto_false_ignored() { let (pkg, ws) = test_paths(); let user_inputs = vec![UserInputEntry::Auto(AutoInput { auto: false })]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert!(!config.includes_auto); assert!(config.positive_globs.is_empty()); assert!(config.negative_globs.is_empty()); @@ -480,7 +492,7 @@ mod tests { UserInputEntry::Glob("src/**/*.ts".into()), UserInputEntry::Glob("package.json".into()), ]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert!(!config.includes_auto); assert_eq!(config.positive_globs.len(), 2); // Globs should now be workspace-root-relative @@ -496,7 +508,7 @@ mod tests { UserInputEntry::Glob("src/**".into()), UserInputEntry::Glob("!src/**/*.test.ts".into()), ]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert!(!config.includes_auto); assert_eq!(config.positive_globs.len(), 1); assert!(config.positive_globs.contains("packages/my-pkg/src/**")); @@ -512,7 +524,7 @@ mod tests { UserInputEntry::Auto(AutoInput { auto: true }), UserInputEntry::Glob("!node_modules/**".into()), ]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert!(config.includes_auto); assert_eq!(config.positive_globs.len(), 1); assert!(config.positive_globs.contains("packages/my-pkg/package.json")); @@ -528,7 +540,7 @@ mod tests { UserInputEntry::Glob("src/**/*.ts".into()), UserInputEntry::Auto(AutoInput { auto: true }), ]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert!(config.includes_auto); } @@ -536,7 +548,7 @@ mod tests { fn test_resolved_input_config_dotdot_resolution() { let (pkg, ws) = test_paths(); let user_inputs = vec![UserInputEntry::Glob("../shared/src/**".into())]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert_eq!(config.positive_globs.len(), 1); assert!( config.positive_globs.contains("packages/shared/src/**"), @@ -549,7 +561,7 @@ mod tests { fn test_resolved_input_config_outside_workspace_error() { let (pkg, ws) = test_paths(); let user_inputs = vec![UserInputEntry::Glob("../../../outside/**".into())]; - let result = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws); + let result = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws); assert!(result.is_err()); assert!(matches!(result.unwrap_err(), ResolveTaskConfigError::GlobOutsideWorkspace { .. })); } @@ -561,7 +573,7 @@ mod tests { pattern: "configs/tsconfig.json".into(), base: InputBase::Workspace, })]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert!(!config.includes_auto); assert_eq!(config.positive_globs.len(), 1); // Workspace-base: should NOT have the package prefix @@ -579,7 +591,7 @@ mod tests { pattern: "!dist/**".into(), base: InputBase::Workspace, })]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert_eq!(config.negative_globs.len(), 1); assert!( config.negative_globs.contains("dist/**"), @@ -596,7 +608,7 @@ mod tests { pattern: "src/**/*.ts".into(), base: InputBase::Package, })]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert_eq!(config.positive_globs.len(), 1); assert!( config.positive_globs.contains("packages/my-pkg/src/**/*.ts"), @@ -620,7 +632,7 @@ mod tests { base: InputBase::Workspace, }), ]; - let config = ResolvedInputConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); + let config = ResolvedGlobConfig::from_user_config(Some(&user_inputs), &pkg, &ws).unwrap(); assert!(config.includes_auto); assert_eq!(config.positive_globs.len(), 2); assert!(config.positive_globs.contains("packages/my-pkg/src/**")); diff --git a/crates/vite_task_graph/src/config/user.rs b/crates/vite_task_graph/src/config/user.rs index 206f57cb..c9c898a3 100644 --- a/crates/vite_task_graph/src/config/user.rs +++ b/crates/vite_task_graph/src/config/user.rs @@ -125,6 +125,18 @@ pub struct EnabledCacheConfig { #[serde(default)] #[cfg_attr(all(test, not(clippy)), ts(inline))] pub input: Option, + + /// Output files to archive and restore on cache hit. + /// + /// - Omitted: automatically tracks which files the task writes + /// - `[]` (empty): disables output restoration entirely + /// - Glob patterns (e.g. `"dist/**"`) select specific output files, relative to the package directory + /// - `{pattern: "...", base: "workspace" | "package"}` specifies a glob with an explicit base directory + /// - `{auto: true}` enables automatic file tracking + /// - Negative patterns (e.g. `"!dist/cache/**"`) exclude matched files + #[serde(default)] + #[cfg_attr(all(test, not(clippy)), ts(inline))] + pub output: Option, } /// Options for user-defined tasks in `vite.config.*`, excluding the command. @@ -160,6 +172,7 @@ impl Default for UserTaskOptions { env: None, untracked_env: None, input: None, + output: None, }, }, } @@ -428,6 +441,7 @@ mod tests { env: Some(std::iter::once("NODE_ENV".into()).collect()), untracked_env: Some(std::iter::once("FOO".into()).collect()), input: None, + output: None, } }, ); diff --git a/crates/vite_task_plan/src/cache_metadata.rs b/crates/vite_task_plan/src/cache_metadata.rs index c3435c34..11a1d957 100644 --- a/crates/vite_task_plan/src/cache_metadata.rs +++ b/crates/vite_task_plan/src/cache_metadata.rs @@ -3,7 +3,7 @@ use std::sync::Arc; use serde::{Deserialize, Serialize}; use vite_path::RelativePathBuf; use vite_str::{self, Str}; -use vite_task_graph::config::ResolvedInputConfig; +use vite_task_graph::config::ResolvedGlobConfig; use wincode::{SchemaRead, SchemaWrite}; use crate::envs::EnvFingerprints; @@ -45,7 +45,11 @@ pub struct CacheMetadata { /// Resolved input configuration for cache fingerprinting. /// Used at execution time to determine what files to track. - pub input_config: ResolvedInputConfig, + pub input_config: ResolvedGlobConfig, + + /// Resolved output configuration for cache restoration. + /// Used at execution time to determine what output files to archive. + pub output_config: ResolvedGlobConfig, } /// Fingerprint for spawn execution that affects caching. diff --git a/crates/vite_task_plan/src/plan.rs b/crates/vite_task_plan/src/plan.rs index 8e657d84..f11f1532 100644 --- a/crates/vite_task_plan/src/plan.rs +++ b/crates/vite_task_plan/src/plan.rs @@ -20,7 +20,7 @@ use vite_str::Str; use vite_task_graph::{ TaskNodeIndex, TaskSource, config::{ - CacheConfig, EnabledCacheConfig, ResolvedGlobalCacheConfig, ResolvedInputConfig, + CacheConfig, EnabledCacheConfig, ResolvedGlobConfig, ResolvedGlobalCacheConfig, ResolvedTaskOptions, user::{UserCacheConfig, UserTaskOptions}, }, @@ -460,7 +460,8 @@ fn resolve_synthetic_cache_config( Ok(match synthetic_cache_config { UserCacheConfig::Disabled { .. } => Option::None, UserCacheConfig::Enabled { enabled_cache_config, .. } => { - let EnabledCacheConfig { env, untracked_env, input } = enabled_cache_config; + let EnabledCacheConfig { env, untracked_env, input, output } = + enabled_cache_config; parent_config.env_config.fingerprinted_envs.extend(env.unwrap_or_default()); parent_config .env_config @@ -468,7 +469,7 @@ fn resolve_synthetic_cache_config( .extend(untracked_env.unwrap_or_default()); if let Some(input) = input { - let synthetic_input = ResolvedInputConfig::from_user_config( + let synthetic_input = ResolvedGlobConfig::from_user_config( Some(&input), package_dir, workspace_path, @@ -487,6 +488,23 @@ fn resolve_synthetic_cache_config( .extend(synthetic_input.negative_globs); } + if let Some(output) = output { + let synthetic_output = ResolvedGlobConfig::from_user_config( + Some(&output), + package_dir, + workspace_path, + ) + .map_err(Error::ResolveTaskConfig)?; + parent_config + .output_config + .positive_globs + .extend(synthetic_output.positive_globs); + parent_config + .output_config + .negative_globs + .extend(synthetic_output.negative_globs); + } + Some(parent_config) } }) @@ -620,6 +638,7 @@ fn plan_spawn_execution( spawn_fingerprint, execution_cache_key, input_config: cache_config.input_config.clone(), + output_config: cache_config.output_config.clone(), }); } } @@ -845,7 +864,7 @@ mod tests { use vite_path::AbsolutePathBuf; use vite_str::Str; use vite_task_graph::config::{ - CacheConfig, EnabledCacheConfig, EnvConfig, ResolvedInputConfig, + CacheConfig, EnabledCacheConfig, EnvConfig, ResolvedGlobConfig, user::{UserCacheConfig, UserInputEntry}, }; @@ -871,11 +890,12 @@ mod tests { fingerprinted_envs: FxHashSet::default(), untracked_env: FxHashSet::default(), }, - input_config: ResolvedInputConfig { + input_config: ResolvedGlobConfig { includes_auto, positive_globs: positive_globs.iter().map(|s| Str::from(*s)).collect(), negative_globs: BTreeSet::new(), }, + output_config: ResolvedGlobConfig::default_auto(), } } @@ -893,6 +913,7 @@ mod tests { env: None, untracked_env: None, input: None, + output: None, }), &pkg, &ws, @@ -914,6 +935,7 @@ mod tests { env: None, untracked_env: None, input: Some(vec![UserInputEntry::Glob("config/**".into())]), + output: None, }), &pkg, &ws, @@ -935,6 +957,7 @@ mod tests { env: None, untracked_env: None, input: Some(vec![UserInputEntry::Glob("config/**".into())]), + output: None, }), &pkg, &ws, @@ -962,6 +985,7 @@ mod tests { UserInputEntry::Glob("config/**".into()), UserInputEntry::Auto(vite_task_graph::config::user::AutoInput { auto: true }), ]), + output: None, }), &pkg, &ws, @@ -987,6 +1011,7 @@ mod tests { env: None, untracked_env: None, input: Some(vec![UserInputEntry::Glob("config/**".into())]), + output: None, }), &pkg, &ws, @@ -1010,6 +1035,7 @@ mod tests { env: None, untracked_env: None, input: Some(vec![UserInputEntry::Glob("config/**".into())]), + output: None, }), &pkg, &ws, @@ -1042,6 +1068,7 @@ mod tests { env: None, untracked_env: None, input: Some(vec![UserInputEntry::Glob("!dist/**".into())]), + output: None, }), &pkg, &ws, diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional_env/snapshots/query_tool_synthetic_task_in_user_task.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional_env/snapshots/query_tool_synthetic_task_in_user_task.jsonc index 9f64c076..70a30a68 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional_env/snapshots/query_tool_synthetic_task_in_user_task.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional_env/snapshots/query_tool_synthetic_task_in_user_task.jsonc @@ -60,6 +60,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional_env/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional_env/snapshots/task_graph.jsonc index 253fae9f..3690168f 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional_env/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/additional_env/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_enables_script_caching.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_enables_script_caching.jsonc index 67b1ae1c..17487e76 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_enables_script_caching.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_enables_script_caching.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_enables_task_caching_even_when_cache_tasks_is_false.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_enables_task_caching_even_when_cache_tasks_is_false.jsonc index 4ceddbc2..be52cb30 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_enables_task_caching_even_when_cache_tasks_is_false.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_enables_task_caching_even_when_cache_tasks_is_false.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_on_task_with_per_task_cache_true_enables_caching.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_on_task_with_per_task_cache_true_enables_caching.jsonc index cf1e3a1e..4c12deaa 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_on_task_with_per_task_cache_true_enables_caching.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/query___cache_on_task_with_per_task_cache_true_enables_caching.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/task_graph.jsonc index feb1a3c6..9f5fe4c2 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_cli_override/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -116,6 +126,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -150,6 +165,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_echo_and_lint_with_extra_args.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_echo_and_lint_with_extra_args.jsonc index 6bcfb7bc..48e0bcd9 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_echo_and_lint_with_extra_args.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_echo_and_lint_with_extra_args.jsonc @@ -87,6 +87,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_lint_and_echo_with_extra_args.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_lint_and_echo_with_extra_args.jsonc index e1997caf..4e1becc6 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_lint_and_echo_with_extra_args.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_lint_and_echo_with_extra_args.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_normal_task_with_extra_args.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_normal_task_with_extra_args.jsonc index e273f579..85eda7c0 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_normal_task_with_extra_args.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_normal_task_with_extra_args.jsonc @@ -60,6 +60,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_in_user_task.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_in_user_task.jsonc index 10fcc3e0..9b026176 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_in_user_task.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_in_user_task.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_in_user_task_with_cwd.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_in_user_task_with_cwd.jsonc index 10fcc3e0..9b026176 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_in_user_task_with_cwd.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_in_user_task_with_cwd.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_with_extra_args_in_user_task.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_with_extra_args_in_user_task.jsonc index 364b3e97..b49b7cda 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_with_extra_args_in_user_task.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/query_synthetic_task_with_extra_args_in_user_task.jsonc @@ -61,6 +61,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/task_graph.jsonc index 2ca6de9a..69c2936d 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_keys/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -128,6 +143,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_default/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_default/snapshots/task_graph.jsonc index 87693078..8b8f3c46 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_default/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_default/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_enabled/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_enabled/snapshots/task_graph.jsonc index bc7667e7..85e08a27 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_enabled/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_enabled/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/query_another_task_cached_by_default.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/query_another_task_cached_by_default.jsonc index 901475f0..96355742 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/query_another_task_cached_by_default.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/query_another_task_cached_by_default.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/query_task_cached_by_default.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/query_task_cached_by_default.jsonc index d34e6f5c..e4922043 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/query_task_cached_by_default.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/query_task_cached_by_default.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/task_graph.jsonc index a5632de7..a6a4e872 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_scripts_task_override/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_sharing/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_sharing/snapshots/task_graph.jsonc index 4688df73..406b3f57 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_sharing/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_sharing/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_subcommand/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_subcommand/snapshots/task_graph.jsonc index b8b92665..3ae013b8 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_subcommand/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_subcommand/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_tasks_disabled/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_tasks_disabled/snapshots/task_graph.jsonc index 1d5d0edf..0e3aa085 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_tasks_disabled/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_tasks_disabled/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/query_script_cached_when_global_cache_true.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/query_script_cached_when_global_cache_true.jsonc index 7d74b65d..bdc8c7ad 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/query_script_cached_when_global_cache_true.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/query_script_cached_when_global_cache_true.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/query_task_cached_when_global_cache_true.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/query_task_cached_when_global_cache_true.jsonc index dbcb6a68..e27d6cc8 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/query_task_cached_when_global_cache_true.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/query_task_cached_when_global_cache_true.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/task_graph.jsonc index b19be370..ae2a02d9 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cache_true_no_force_enable/snapshots/task_graph.jsonc @@ -48,6 +48,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -82,6 +87,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/query_cd_before_vt_lint_should_put_synthetic_task_under_cwd.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/query_cd_before_vt_lint_should_put_synthetic_task_under_cwd.jsonc index 1f463667..7e658be2 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/query_cd_before_vt_lint_should_put_synthetic_task_under_cwd.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/query_cd_before_vt_lint_should_put_synthetic_task_under_cwd.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/query_cd_before_vt_run_should_not_affect_expanded_task_cwd.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/query_cd_before_vt_run_should_not_affect_expanded_task_cwd.jsonc index 93c1d00e..3504841c 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/query_cd_before_vt_run_should_not_affect_expanded_task_cwd.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/query_cd_before_vt_run_should_not_affect_expanded_task_cwd.jsonc @@ -84,6 +84,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/task_graph.jsonc index 4fc7ccaa..09b02be1 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cd_in_scripts/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/comprehensive_task_graph/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/comprehensive_task_graph/snapshots/task_graph.jsonc index c02cdb6e..2d5f8307 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/comprehensive_task_graph/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/comprehensive_task_graph/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -128,6 +143,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -162,6 +182,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -196,6 +221,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -230,6 +260,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -264,6 +299,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -298,6 +338,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -332,6 +377,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -366,6 +416,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -400,6 +455,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -434,6 +494,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -468,6 +533,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -502,6 +572,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -536,6 +611,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -570,6 +650,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -604,6 +689,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -638,6 +728,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -672,6 +767,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -706,6 +806,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -740,6 +845,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -774,6 +884,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/conflict_test/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/conflict_test/snapshots/task_graph.jsonc index 9b82411d..f88266a4 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/conflict_test/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/conflict_test/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cycle_dependency/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cycle_dependency/snapshots/task_graph.jsonc index 6336fe8a..02744a92 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/cycle_dependency/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/cycle_dependency/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -65,6 +70,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/dependency_both_topo_and_explicit/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/dependency_both_topo_and_explicit/snapshots/task_graph.jsonc index ae123672..6cd676ac 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/dependency_both_topo_and_explicit/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/dependency_both_topo_and_explicit/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -65,6 +70,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/duplicate_package_names/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/duplicate_package_names/snapshots/task_graph.jsonc index 2b1c82e4..dc309692 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/duplicate_package_names/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/duplicate_package_names/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/empty_package_test/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/empty_package_test/snapshots/task_graph.jsonc index 726f2938..15f5fee2 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/empty_package_test/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/empty_package_test/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -100,6 +105,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -134,6 +144,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -168,6 +183,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -207,6 +227,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -241,6 +266,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -275,6 +305,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -309,6 +344,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/explicit_deps_workspace/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/explicit_deps_workspace/snapshots/task_graph.jsonc index 3d0fc433..258dbe37 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/explicit_deps_workspace/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/explicit_deps_workspace/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -95,6 +100,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -129,6 +139,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -163,6 +178,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -197,6 +217,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -231,6 +256,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -270,6 +300,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -304,6 +339,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -338,6 +378,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -381,6 +426,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/extra_args_not_forwarded_to_depends_on/snapshots/query_extra_args_only_reach_requested_task.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/extra_args_not_forwarded_to_depends_on/snapshots/query_extra_args_only_reach_requested_task.jsonc index a6b86de0..87c2c445 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/extra_args_not_forwarded_to_depends_on/snapshots/query_extra_args_only_reach_requested_task.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/extra_args_not_forwarded_to_depends_on/snapshots/query_extra_args_only_reach_requested_task.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { @@ -140,6 +145,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/extra_args_not_forwarded_to_depends_on/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/extra_args_not_forwarded_to_depends_on/snapshots/task_graph.jsonc index a110c9d8..0f8a5a02 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/extra_args_not_forwarded_to_depends_on/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/extra_args_not_forwarded_to_depends_on/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/filter_workspace/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/filter_workspace/snapshots/task_graph.jsonc index 13011495..749052ab 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/filter_workspace/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/filter_workspace/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -128,6 +143,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -162,6 +182,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -196,6 +221,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -230,6 +260,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -264,6 +299,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -298,6 +338,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -332,6 +377,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -366,6 +416,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -400,6 +455,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -434,6 +494,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/input_trailing_slash/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/input_trailing_slash/snapshots/task_graph.jsonc index a793212e..cc3c5380 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/input_trailing_slash/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/input_trailing_slash/snapshots/task_graph.jsonc @@ -30,6 +30,11 @@ "negative_globs": [ "dist/**" ] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/input_workspace_base/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/input_workspace_base/snapshots/task_graph.jsonc index 03b8005d..f8a2b06f 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/input_workspace_base/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/input_workspace_base/snapshots/task_graph.jsonc @@ -31,6 +31,11 @@ "negative_globs": [ "dist/**" ] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_nested___cache_enables_inner_task_caching.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_nested___cache_enables_inner_task_caching.jsonc index e1b6e4bb..bb31ec14 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_nested___cache_enables_inner_task_caching.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_nested___cache_enables_inner_task_caching.jsonc @@ -84,6 +84,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_outer___cache_propagates_to_nested_run_without_flags.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_outer___cache_propagates_to_nested_run_without_flags.jsonc index d3ba94f6..2429e6f5 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_outer___cache_propagates_to_nested_run_without_flags.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_outer___cache_propagates_to_nested_run_without_flags.jsonc @@ -84,6 +84,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_outer___no_cache_does_not_propagate_into_nested___cache.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_outer___no_cache_does_not_propagate_into_nested___cache.jsonc index bfd03358..2239dc90 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_outer___no_cache_does_not_propagate_into_nested___cache.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/query_outer___no_cache_does_not_propagate_into_nested___cache.jsonc @@ -84,6 +84,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/task_graph.jsonc index 269edec0..d1f6cf7a 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_cache_override/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -128,6 +143,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_tasks/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_tasks/snapshots/task_graph.jsonc index 8492756a..84427555 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_tasks/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/nested_tasks/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/package_self_dependency/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/package_self_dependency/snapshots/task_graph.jsonc index 837fc00b..0fa96e98 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/package_self_dependency/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/package_self_dependency/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/parallel_and_concurrency/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/parallel_and_concurrency/snapshots/task_graph.jsonc index 5c4ce9eb..3310dddd 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/parallel_and_concurrency/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/parallel_and_concurrency/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -128,6 +143,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -162,6 +182,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/pnpm_workspace_packages_optional/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/pnpm_workspace_packages_optional/snapshots/task_graph.jsonc index 8c09f955..7c721c0c 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/pnpm_workspace_packages_optional/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/pnpm_workspace_packages_optional/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/recursive_topological_workspace/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/recursive_topological_workspace/snapshots/task_graph.jsonc index 49781abe..721994b0 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/recursive_topological_workspace/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/recursive_topological_workspace/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -128,6 +143,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -162,6 +182,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -196,6 +221,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -230,6 +260,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -264,6 +299,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks/snapshots/task_graph.jsonc index bb5913bb..74c4fb4d 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -128,6 +143,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -162,6 +182,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -196,6 +221,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_disabled/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_disabled/snapshots/task_graph.jsonc index 43f2ac98..2d639083 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_disabled/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_disabled/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_nested_run/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_nested_run/snapshots/task_graph.jsonc index b7db1210..89b34531 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_nested_run/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_nested_run/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -128,6 +143,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_task_no_hook/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_task_no_hook/snapshots/task_graph.jsonc index a70ddee4..a7a33890 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_task_no_hook/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/script_hooks_task_no_hook/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell_fallback/snapshots/query_shell_fallback_for_pipe_command.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell_fallback/snapshots/query_shell_fallback_for_pipe_command.jsonc index 738582f7..c1fadfcf 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell_fallback/snapshots/query_shell_fallback_for_pipe_command.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell_fallback/snapshots/query_shell_fallback_for_pipe_command.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell_fallback/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell_fallback/snapshots/task_graph.jsonc index c0d1aba3..c8d4d282 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell_fallback/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/shell_fallback/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_parent_cache_false_does_not_affect_expanded_query_tasks.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_parent_cache_false_does_not_affect_expanded_query_tasks.jsonc index 06e4fc59..a706bf22 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_parent_cache_false_does_not_affect_expanded_query_tasks.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_parent_cache_false_does_not_affect_expanded_query_tasks.jsonc @@ -84,6 +84,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_script_cache_false_does_not_affect_expanded_synthetic_cache.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_script_cache_false_does_not_affect_expanded_synthetic_cache.jsonc index a6bbadea..07de08fd 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_script_cache_false_does_not_affect_expanded_synthetic_cache.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_script_cache_false_does_not_affect_expanded_synthetic_cache.jsonc @@ -84,6 +84,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_task_untrackedEnv_inherited_by_synthetic.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_task_untrackedEnv_inherited_by_synthetic.jsonc index b36b29e2..e8ecc3a8 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_task_untrackedEnv_inherited_by_synthetic.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_task_untrackedEnv_inherited_by_synthetic.jsonc @@ -59,6 +59,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_task_with_cache_true_enables_synthetic_cache.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_task_with_cache_true_enables_synthetic_cache.jsonc index c3e7c5ff..6c8b888d 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_task_with_cache_true_enables_synthetic_cache.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/query_task_with_cache_true_enables_synthetic_cache.jsonc @@ -58,6 +58,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/task_graph.jsonc index b1cfddc1..28255bc3 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_cache_disabled/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -116,6 +126,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -151,6 +166,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -185,6 +205,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_in_subpackage/snapshots/query_synthetic_in_subpackage.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_in_subpackage/snapshots/query_synthetic_in_subpackage.jsonc index 2c60292b..073de375 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_in_subpackage/snapshots/query_synthetic_in_subpackage.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_in_subpackage/snapshots/query_synthetic_in_subpackage.jsonc @@ -84,6 +84,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } }, "spawn_command": { diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_in_subpackage/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_in_subpackage/snapshots/task_graph.jsonc index 554d3ab0..29978073 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_in_subpackage/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/synthetic_in_subpackage/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/transitive_skip_intermediate/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/transitive_skip_intermediate/snapshots/task_graph.jsonc index 23a84412..281260a0 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/transitive_skip_intermediate/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/transitive_skip_intermediate/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr_shorthand/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr_shorthand/snapshots/task_graph.jsonc index 0ae0ad7d..a4fbd68c 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr_shorthand/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/vpr_shorthand/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_cd_no_skip/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_cd_no_skip/snapshots/task_graph.jsonc index 5a9820d5..f105a656 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_cd_no_skip/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_cd_no_skip/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_depends_on_passthrough/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_depends_on_passthrough/snapshots/task_graph.jsonc index 68302384..b6b8e743 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_depends_on_passthrough/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_depends_on_passthrough/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -65,6 +70,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -99,6 +109,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -133,6 +148,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_multi_command/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_multi_command/snapshots/task_graph.jsonc index 1cd3ba45..30236c51 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_multi_command/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_multi_command/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_mutual_recursion/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_mutual_recursion/snapshots/task_graph.jsonc index 1e0d1452..4cb03238 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_mutual_recursion/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_mutual_recursion/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -128,6 +143,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_no_package_json/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_no_package_json/snapshots/task_graph.jsonc index 56576d86..a9be2e81 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_no_package_json/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_no_package_json/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_self_reference/snapshots/task_graph.jsonc b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_self_reference/snapshots/task_graph.jsonc index 0b4cbfe8..2445ccfb 100644 --- a/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_self_reference/snapshots/task_graph.jsonc +++ b/crates/vite_task_plan/tests/plan_snapshots/fixtures/workspace_root_self_reference/snapshots/task_graph.jsonc @@ -26,6 +26,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -60,6 +65,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } @@ -94,6 +104,11 @@ "includes_auto": true, "positive_globs": [], "negative_globs": [] + }, + "output_config": { + "includes_auto": true, + "positive_globs": [], + "negative_globs": [] } } } diff --git a/docs/output-restoration-research.md b/docs/output-restoration-research.md new file mode 100644 index 00000000..4f6beda8 --- /dev/null +++ b/docs/output-restoration-research.md @@ -0,0 +1,117 @@ +# Output Restoration: Compatibility with Real Build Tools + +## Background + +Output restoration automatically archives files produced by a cached task and restores them on cache hit. +When a task runs and creates `dist/`, those files are saved as a `tar.zst` archive in the cache directory. +On subsequent cache hits the archive is extracted, skipping the need to re-execute the task. + +The feature works end-to-end when tested with simple write-only commands (`vtt write-file dist/out.txt built`), +but **fails with real build tools** (Vite 8, tsdown) in the default auto-detection mode. + +## The Problem + +Deleting the output directory (`dist/`) between runs causes a **cache miss** instead of triggering output restoration. + +``` +~/packages/vite-app$ vite build +... +✓ built in 34ms + +# delete dist, run again: + +~/packages/vite-app$ vite build ○ cache miss: 'assets' removed from 'packages/vite-app/dist', executing +``` + +The archived output files exist in the cache and could be restored, but the cache validation rejects the entry before restoration has a chance to run. + +## Root Cause + +Build tools **read from their output directory** during execution. fspy captures these reads and records them as inferred inputs. When the output directory is later deleted, the inferred input fingerprint no longer matches, producing a cache miss. + +### How the cache validation pipeline works + +1. **Cache entry lookup** — match by `CacheEntryKey` (spawn fingerprint + input config + output config) +2. **Globbed input validation** — compare stored file hashes against current state for explicit input globs +3. **Post-run fingerprint validation** — compare stored fspy-inferred input fingerprints against current filesystem state +4. **If all pass** → cache hit → replay terminal output → **extract output archive** + +The failure occurs at step 3. The stored fingerprint records `packages/app/dist` as `Folder(Some({assets: Dir, index.html: File}))`. After deletion the current fingerprint is `NotFound`. The mismatch is reported before output restoration at step 4 can execute. + +### Why build tools read from the output directory + +Both Vite 8 and tsdown follow the same pattern: **clean the output directory before writing new files**, then **report compressed sizes after writing**. + +#### Vite 8 (`vite build`) + +1. **Directory cleanup** (`emptyDir()`, called from `prepareOutDirPlugin` during `renderStart`) + - `fs.readdirSync(outDir)` enumerates entries so they can be deleted before the new build + - Controlled by `emptyOutDir` (default: `true`) + - This is the primary read that causes `packages/app/dist` to appear in fspy's `path_reads` + +2. **Compressed size reporting** (rolldown's builtin `viteReporterPlugin`, Rust-native) + - After writing output files, the reporter reads each file back to compute gzip/brotli sizes + - Produces the `dist/index.html 0.15 kB │ gzip: 0.14 kB` lines + - Controlled by `reportCompressedSize` (default: `true`) + - This causes reads on individual output files like `dist/index.html`, `dist/assets/index-xxx.js` + +3. **Public directory copy** (`copyDir()`) + - If `copyPublicDir` is enabled (default: `true`), reads the public dir to copy into outDir + +#### tsdown + +1. **Directory cleanup** (`cleanOutDir()` via `tinyglobby`'s `glob()`) + - Enumerates all files in the output directory with `onlyFiles: false` before deleting them + - Default `clean: true` triggers this + - This is the primary read that causes `packages/lib/dist` to appear in fspy's `path_reads` + +2. **Shebang permission check** (`ShebangPlugin`'s `writeBundle` hook) + - Calls `access()` on output files to check existence before setting execute permissions + - Only applies to entry chunks with shebang directives + +### Why the read-write overlap check doesn't catch this + +The overlap check at `execute_spawn` (mod.rs:486-488) looks for exact path matches between `path_reads` and `path_writes`: + +```rust +pa.path_reads.keys().find(|p| pa.path_writes.contains(*p)) +``` + +fspy reports these as separate paths: + +- **Read**: `packages/app/dist` (the directory itself, via `readdirSync`) +- **Write**: `packages/app/dist/index.html`, `packages/app/dist/assets/index-xxx.js` (individual files) + +Since `packages/app/dist` ≠ `packages/app/dist/index.html`, no overlap is detected, and caching proceeds. + +### Why `should_ignore_entry` doesn't help + +`fingerprint.rs:185-187` filters `dist` when listed as a directory entry of a **parent**: + +```rust +fn should_ignore_entry(name: &[u8]) -> bool { + matches!(name, b"." | b".." | b".DS_Store") || name.eq_ignore_ascii_case(b"dist") +} +``` + +This prevents the fingerprint of `packages/app/` from changing when `dist` appears or disappears inside it. But it does not help when `packages/app/dist` itself is a direct key in `inferred_inputs` — that path is fingerprinted independently, and its transition from `Folder(...)` to `NotFound` is a mismatch. + +### Why the existing e2e test passes + +The `output-cache-test` fixture uses `vtt write-file dist/out.txt built`, a simple write-only operation. `vtt write-file` never reads from `dist/`, so fspy only records it as a write. The directory never appears in `path_reads`, the post-run fingerprint doesn't include it, and cache validation succeeds after deletion. + +This does not reflect real build tool behavior. + +## Behavior Matrix + +All tests performed with Vite 8.0.8 and tsdown 0.12.9. "Cache hit after deleting dist" means output restoration can work. + +| `input` | `output` | fspy enabled | Cache hit after deleting dist | +| -------------- | --------------- | ------------ | ----------------------------- | +| auto (default) | auto (default) | yes | **no** | +| auto (default) | `["dist/**"]` | yes | **no** | +| `["src/**"]` | auto (default) | yes | **no** | +| `["src/**"]` | `["dist/**"]` | no | **yes** | +| `["src/**"]` | `[]` (disabled) | no | yes (but no restoration) | + +The only configuration that works requires **both** explicit input globs **and** explicit output globs, which disables fspy entirely. Any configuration that enables fspy (for either auto-input or auto-output detection) causes the output directory reads to pollute the inferred input set. diff --git a/docs/output-restoration.md b/docs/output-restoration.md new file mode 100644 index 00000000..5b004e75 --- /dev/null +++ b/docs/output-restoration.md @@ -0,0 +1,120 @@ +# Output Restoration + +When a cached task is replayed, vp doesn't just replay the terminal output — it also restores any files the task produced. So if your `build` task writes to `dist/`, a cache hit will put those files right back where they belong. + +## How It Works + +By default, vp watches which files a task writes during execution. On the next run, if the cache hits, those files are extracted back into the workspace automatically. You don't need to configure anything. + +```json +{ + "tasks": { + "build": { + "command": "tsc --outDir dist" + } + } +} +``` + +After `vp run build` runs once, the compiled files in `dist/` are archived. On subsequent runs that hit the cache, `dist/` is restored from that archive instead of recompiling. + +## The `output` Field + +The `output` field lets you control which files get archived and restored. It works exactly like `input` — same glob patterns, same `auto` directive, same negative patterns. + +### Default (omitted) + +Auto-detects written files. This is usually all you need: + +```json +{ + "tasks": { + "build": { + "command": "tsc --outDir dist" + } + } +} +``` + +### Explicit Globs + +If you only want specific files restored: + +```json +{ + "tasks": { + "build": { + "command": "tsc --outDir dist", + "output": ["dist/**"] + } + } +} +``` + +This ignores any other files the task might write (temp files, logs, etc.) and only archives what's under `dist/`. + +### Negative Patterns + +Exclude certain output files from being cached: + +```json +{ + "tasks": { + "build": { + "command": "webpack", + "output": [{ "auto": true }, "!dist/cache/**"] + } + } +} +``` + +Here, everything the task writes is archived _except_ files under `dist/cache/`. + +### Disable Output Restoration + +If you don't want any files restored on cache hit — only terminal output replayed — pass an empty array: + +```json +{ + "tasks": { + "test": { + "command": "vitest run", + "output": [] + } + } +} +``` + +## `output` and `input` Are Independent + +The `output` field has its own auto-detection, separate from `input`. You can disable auto-inference for inputs while keeping it for outputs: + +```json +{ + "tasks": { + "build": { + "command": "tsc --outDir dist", + "input": ["src/**/*.ts", "tsconfig.json"], + "output": [{ "auto": true }] + } + } +} +``` + +This tracks inputs via explicit globs only (no file-read inference), but still auto-detects which files the task writes for output restoration. + +## Configuration Summary + +| Configuration | Behavior | +| ------------------------------------- | ----------------------------------- | +| `output` omitted | Auto-detect written files (default) | +| `output: [{ "auto": true }]` | Same as omitted | +| `output: ["dist/**"]` | Only restore files under `dist/` | +| `output: [{ "auto": true }, "!tmp/"]` | Auto-detect, but skip `tmp/` | +| `output: []` | Don't restore any files | + +## Notes + +- Changing the `output` configuration invalidates the cache — if you switch from `["dist/**"]` to `["build/**"]`, the next run will be a cache miss. +- Output archives are stored alongside the cache database. Running `vp cache clean` removes them. +- The `output` field can't be used with `cache: false`, same as `input`.