-
Notifications
You must be signed in to change notification settings - Fork 30.3k
feat: add Claude Code plugin marketplace with Cache Components skill #87993
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Stats from current PRDefault Build (Increase detected
|
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| buildDuration | 17.6s | 15.4s | N/A |
| buildDurationCached | 14.4s | 11.4s | N/A |
| nodeModulesSize | 457 MB | 457 MB | ✓ |
| nextStartRea..uration (ms) | 708ms | 690ms | N/A |
Client Bundles (main, webpack) Overall increase ⚠️
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| 2086.HASH.js gzip | 169 B | 169 B | ✓ |
| 2161-HASH.js gzip | 5.39 kB | 5.41 kB | N/A |
| 2747-HASH.js gzip | 4.48 kB | 4.46 kB | N/A |
| 4322-HASH.js gzip | 52.5 kB | 52.2 kB | N/A |
| ec793fe8-HASH.js gzip | 62.3 kB | 62.3 kB | N/A |
| framework-HASH.js gzip | 59.8 kB | 59.8 kB | N/A |
| main-app-HASH.js gzip | 251 B | 253 B | N/A |
| main-HASH.js gzip | 38.4 kB | 38.7 kB | |
| webpack-HASH.js gzip | 1.68 kB | 1.69 kB | N/A |
| Overall change | 38.5 kB | 38.9 kB |
Legacy Client Bundles (polyfills)
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| polyfills-HASH.js gzip | 39.4 kB | 39.4 kB | ✓ |
| Overall change | 39.4 kB | 39.4 kB | ✓ |
Client Pages
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| _app-HASH.js gzip | 194 B | 193 B | N/A |
| _error-HASH.js gzip | 182 B | 182 B | ✓ |
| css-HASH.js gzip | 336 B | 335 B | N/A |
| dynamic-HASH.js gzip | 1.8 kB | 1.8 kB | N/A |
| edge-ssr-HASH.js gzip | 256 B | 256 B | ✓ |
| head-HASH.js gzip | 352 B | 349 B | N/A |
| hooks-HASH.js gzip | 385 B | 384 B | N/A |
| image-HASH.js gzip | 580 B | 580 B | ✓ |
| index-HASH.js gzip | 259 B | 258 B | N/A |
| link-HASH.js gzip | 2.5 kB | 2.51 kB | N/A |
| routerDirect..HASH.js gzip | 319 B | 317 B | N/A |
| script-HASH.js gzip | 385 B | 387 B | N/A |
| withRouter-HASH.js gzip | 316 B | 315 B | N/A |
| 1afbb74e6ecf..834.css gzip | 106 B | 106 B | ✓ |
| Overall change | 1.12 kB | 1.12 kB | ✓ |
Client Build Manifests
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| _buildManifest.js gzip | 738 B | 738 B | ✓ |
| Overall change | 738 B | 738 B | ✓ |
Rendered Page Sizes
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| index.html gzip | 523 B | 525 B | N/A |
| link.html gzip | 537 B | 539 B | N/A |
| withRouter.html gzip | 520 B | 521 B | N/A |
| Overall change | 0 B | 0 B | ✓ |
Edge SSR bundle Size Overall increase ⚠️
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| edge-ssr.js gzip | 124 kB | 124 kB | N/A |
| page.js gzip | 238 kB | 239 kB | |
| Overall change | 238 kB | 239 kB |
Middleware size Overall increase ⚠️
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| middleware-b..fest.js gzip | 652 B | 656 B | N/A |
| middleware-r..fest.js gzip | 155 B | 156 B | N/A |
| middleware.js gzip | 32.8 kB | 33 kB | |
| edge-runtime..pack.js gzip | 846 B | 846 B | ✓ |
| Overall change | 33.6 kB | 33.8 kB |
Next Runtimes
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| app-page-exp...dev.js gzip | 302 kB | 302 kB | ✓ |
| app-page-exp..prod.js gzip | 157 kB | 157 kB | ✓ |
| app-page-tur...dev.js gzip | 302 kB | 302 kB | ✓ |
| app-page-tur..prod.js gzip | 157 kB | 157 kB | ✓ |
| app-page-tur...dev.js gzip | 299 kB | 299 kB | N/A |
| app-page-tur..prod.js gzip | 155 kB | 155 kB | ✓ |
| app-page.run...dev.js gzip | 299 kB | 299 kB | ✓ |
| app-page.run..prod.js gzip | 155 kB | 155 kB | ✓ |
| app-route-ex...dev.js gzip | 68.7 kB | 68.7 kB | ✓ |
| app-route-ex..prod.js gzip | 47.5 kB | 47.5 kB | ✓ |
| app-route-tu...dev.js gzip | 68.7 kB | 68.7 kB | ✓ |
| app-route-tu..prod.js gzip | 47.5 kB | 47.5 kB | ✓ |
| app-route-tu...dev.js gzip | 68.3 kB | 68.3 kB | ✓ |
| app-route-tu..prod.js gzip | 47.3 kB | 47.3 kB | ✓ |
| app-route.ru...dev.js gzip | 68.3 kB | 68.3 kB | ✓ |
| app-route.ru..prod.js gzip | 47.3 kB | 47.3 kB | ✓ |
| dist_client_...dev.js gzip | 324 B | 324 B | ✓ |
| dist_client_...dev.js gzip | 326 B | 326 B | ✓ |
| dist_client_...dev.js gzip | 318 B | 318 B | ✓ |
| dist_client_...dev.js gzip | 317 B | 317 B | ✓ |
| pages-api-tu...dev.js gzip | 41.1 kB | 41.1 kB | ✓ |
| pages-api-tu..prod.js gzip | 31.2 kB | 31.2 kB | ✓ |
| pages-api.ru...dev.js gzip | 41 kB | 41 kB | ✓ |
| pages-api.ru..prod.js gzip | 31.2 kB | 31.2 kB | ✓ |
| pages-turbo....dev.js gzip | 50.7 kB | 50.7 kB | ✓ |
| pages-turbo...prod.js gzip | 38.2 kB | 38.2 kB | ✓ |
| pages.runtim...dev.js gzip | 50.6 kB | 50.6 kB | ✓ |
| pages.runtim..prod.js gzip | 38.1 kB | 38.1 kB | ✓ |
| server.runti..prod.js gzip | 59.9 kB | 59.9 kB | ✓ |
| Overall change | 2.38 MB | 2.38 MB | ✓ |
build cache Overall increase ⚠️
| vercel/next.js canary | vercel/next.js feat/claude-code-cache-components-plugin | Change | |
|---|---|---|---|
| 0.pack gzip | 3.62 MB | 3.62 MB | |
| index.pack gzip | 98.5 kB | 98.6 kB | N/A |
| Overall change | 3.62 MB | 3.62 MB |
Diff details
Diff for page.js
Diff too large to display
Diff for middleware.js
Diff too large to display
Diff for edge-ssr.js
Diff too large to display
Diff for _buildManifest.js
@@ -611,35 +611,35 @@ self.__BUILD_MANIFEST = (function (a, b, c) {
numHashes: NaN,
bitArray: [],
},
- "/": ["static\u002Fchunks\u002Fpages\u002Findex-dda7d8b64d4ba15c.js"],
+ "/": ["static\u002Fchunks\u002Fpages\u002Findex-d95f7ec6af4d2644.js"],
"/_error": [
- "static\u002Fchunks\u002Fpages\u002F_error-6ef44d3954f25711.js",
+ "static\u002Fchunks\u002Fpages\u002F_error-209c0c82205a7c9f.js",
],
"/css": [
"static\u002Fcss\u002Fded6b86ab9cc0a1f.css",
- "static\u002Fchunks\u002Fpages\u002Fcss-c8aaa7211416a045.js",
+ "static\u002Fchunks\u002Fpages\u002Fcss-62710339bc830ded.js",
],
"/dynamic": [
- "static\u002Fchunks\u002Fpages\u002Fdynamic-d53bb7f318f342c2.js",
+ "static\u002Fchunks\u002Fpages\u002Fdynamic-8ed3486bb68dd6c6.js",
],
"/edge-ssr": [
- "static\u002Fchunks\u002Fpages\u002Fedge-ssr-1383106d4a3e7d72.js",
+ "static\u002Fchunks\u002Fpages\u002Fedge-ssr-0db9f7bb610d3072.js",
],
- "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-1db1c4be1a45662f.js"],
- "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-e6deee5b72a5b112.js"],
+ "/head": ["static\u002Fchunks\u002Fpages\u002Fhead-747416c4075e42aa.js"],
+ "/hooks": ["static\u002Fchunks\u002Fpages\u002Fhooks-8b10205be7505244.js"],
"/image": [
- "static\u002Fchunks\u002F2747-90c828280005c0c3.js",
- "static\u002Fchunks\u002Fpages\u002Fimage-868472cd5e84efc9.js",
+ "static\u002Fchunks\u002F6349-ee9aecde860d4832.js",
+ "static\u002Fchunks\u002Fpages\u002Fimage-f90ae17c3ad1d38b.js",
],
- "/link": ["static\u002Fchunks\u002Fpages\u002Flink-0ec374e48b2ce5d9.js"],
+ "/link": ["static\u002Fchunks\u002Fpages\u002Flink-6ab9a67b348df1bf.js"],
"/routerDirect": [
- "static\u002Fchunks\u002Fpages\u002FrouterDirect-1a34bfadbc088491.js",
+ "static\u002Fchunks\u002Fpages\u002FrouterDirect-f132fb471e65a8b9.js",
],
"/script": [
- "static\u002Fchunks\u002Fpages\u002Fscript-52320f59afbd096d.js",
+ "static\u002Fchunks\u002Fpages\u002Fscript-8e2028e44b8da2d9.js",
],
"/withRouter": [
- "static\u002Fchunks\u002Fpages\u002FwithRouter-3b1a16d3dfa21c16.js",
+ "static\u002Fchunks\u002Fpages\u002FwithRouter-a86d259faa012c1c.js",
],
sortedPages: [
"\u002F",Diff for dynamic-HASH.js
@@ -1,17 +1,24 @@
(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
[2291],
{
- /***/ 1231: /***/ (
- module,
+ /***/ 2604: /***/ (
+ __unused_webpack_module,
__unused_webpack_exports,
__webpack_require__
) => {
- module.exports = __webpack_require__(4464);
+ (window.__NEXT_P = window.__NEXT_P || []).push([
+ "/dynamic",
+ function () {
+ return __webpack_require__(3643);
+ },
+ ]);
+ if (false) {
+ }
/***/
},
- /***/ 2101: /***/ (
+ /***/ 3401: /***/ (
__unused_webpack_module,
exports,
__webpack_require__
@@ -38,24 +45,63 @@
/***/
},
- /***/ 3458: /***/ (
+ /***/ 3643: /***/ (
__unused_webpack_module,
- __unused_webpack_exports,
+ __webpack_exports__,
__webpack_require__
) => {
- (window.__NEXT_P = window.__NEXT_P || []).push([
- "/dynamic",
- function () {
- return __webpack_require__(9381);
- },
- ]);
- if (false) {
- }
+ "use strict";
+ __webpack_require__.r(__webpack_exports__);
+ /* harmony export */ __webpack_require__.d(__webpack_exports__, {
+ /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
+ /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
+ /* harmony export */
+ });
+ /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
+ __webpack_require__(3108);
+ /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1__ =
+ __webpack_require__(3973);
+ /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1___default =
+ /*#__PURE__*/ __webpack_require__.n(
+ next_dynamic__WEBPACK_IMPORTED_MODULE_1__
+ );
+
+ const DynamicHello = next_dynamic__WEBPACK_IMPORTED_MODULE_1___default()(
+ () =>
+ __webpack_require__
+ .e(/* import() */ 1596)
+ .then(__webpack_require__.bind(__webpack_require__, 1596))
+ .then((mod) => mod.Hello),
+ {
+ loadableGenerated: {
+ webpack: () => [/*require.resolve*/ 1596],
+ },
+ }
+ );
+ const Page = () =>
+ /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(
+ react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragment,
+ {
+ children: [
+ /*#__PURE__*/ (0,
+ react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
+ children: "testing next/dynamic size",
+ }),
+ /*#__PURE__*/ (0,
+ react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
+ DynamicHello,
+ {}
+ ),
+ ],
+ }
+ );
+ var __N_SSP = true;
+ /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = Page;
/***/
},
- /***/ 4464: /***/ (module, exports, __webpack_require__) => {
+ /***/ 3660: /***/ (module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -88,7 +134,7 @@
__webpack_require__(4312)
);
const _loadablesharedruntime = /*#__PURE__*/ _interop_require_default._(
- __webpack_require__(9799)
+ __webpack_require__(4539)
);
const isServerSide = "object" === "undefined";
// Normalize loader to return the module as form { default: Component } for `React.lazy`.
@@ -188,63 +234,17 @@
/***/
},
- /***/ 9381: /***/ (
- __unused_webpack_module,
- __webpack_exports__,
+ /***/ 3973: /***/ (
+ module,
+ __unused_webpack_exports,
__webpack_require__
) => {
- "use strict";
- __webpack_require__.r(__webpack_exports__);
- /* harmony export */ __webpack_require__.d(__webpack_exports__, {
- /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
- /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
- /* harmony export */
- });
- /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
- __webpack_require__(3108);
- /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1__ =
- __webpack_require__(1231);
- /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1___default =
- /*#__PURE__*/ __webpack_require__.n(
- next_dynamic__WEBPACK_IMPORTED_MODULE_1__
- );
-
- const DynamicHello = next_dynamic__WEBPACK_IMPORTED_MODULE_1___default()(
- () =>
- __webpack_require__
- .e(/* import() */ 2086)
- .then(__webpack_require__.bind(__webpack_require__, 2086))
- .then((mod) => mod.Hello),
- {
- loadableGenerated: {
- webpack: () => [/*require.resolve*/ 2086],
- },
- }
- );
- const Page = () =>
- /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(
- react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragment,
- {
- children: [
- /*#__PURE__*/ (0,
- react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
- children: "testing next/dynamic size",
- }),
- /*#__PURE__*/ (0,
- react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
- DynamicHello,
- {}
- ),
- ],
- }
- );
- var __N_SSP = true;
- /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = Page;
+ module.exports = __webpack_require__(3660);
/***/
},
- /***/ 9799: /***/ (
+ /***/ 4539: /***/ (
__unused_webpack_module,
exports,
__webpack_require__
@@ -286,7 +286,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
const _react = /*#__PURE__*/ _interop_require_default._(
__webpack_require__(4312)
);
- const _loadablecontextsharedruntime = __webpack_require__(2101);
+ const _loadablecontextsharedruntime = __webpack_require__(3401);
function resolve(obj) {
return obj && obj.default ? obj.default : obj;
}
@@ -524,7 +524,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
/******/ var __webpack_exec__ = (moduleId) =>
__webpack_require__((__webpack_require__.s = moduleId));
/******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
- __webpack_exec__(3458)
+ __webpack_exec__(2604)
);
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ _N_E = __webpack_exports__;Diff for edge-ssr-HASH.js
@@ -1,7 +1,24 @@
(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
[676],
{
- /***/ 1564: /***/ (
+ /***/ 7046: /***/ (
+ __unused_webpack_module,
+ __unused_webpack_exports,
+ __webpack_require__
+ ) => {
+ (window.__NEXT_P = window.__NEXT_P || []).push([
+ "/edge-ssr",
+ function () {
+ return __webpack_require__(7142);
+ },
+ ]);
+ if (false) {
+ }
+
+ /***/
+ },
+
+ /***/ 7142: /***/ (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
@@ -20,30 +37,13 @@
/***/
},
-
- /***/ 4300: /***/ (
- __unused_webpack_module,
- __unused_webpack_exports,
- __webpack_require__
- ) => {
- (window.__NEXT_P = window.__NEXT_P || []).push([
- "/edge-ssr",
- function () {
- return __webpack_require__(1564);
- },
- ]);
- if (false) {
- }
-
- /***/
- },
},
/******/ (__webpack_require__) => {
// webpackRuntimeModules
/******/ var __webpack_exec__ = (moduleId) =>
__webpack_require__((__webpack_require__.s = moduleId));
/******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
- __webpack_exec__(4300)
+ __webpack_exec__(7046)
);
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ _N_E = __webpack_exports__;Diff for head-HASH.js
@@ -1,34 +1,17 @@
(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
[5350],
{
- /***/ 1548: /***/ (
- __unused_webpack_module,
- __unused_webpack_exports,
- __webpack_require__
- ) => {
- (window.__NEXT_P = window.__NEXT_P || []).push([
- "/head",
- function () {
- return __webpack_require__(4282);
- },
- ]);
- if (false) {
- }
-
- /***/
- },
-
- /***/ 3952: /***/ (
+ /***/ 3770: /***/ (
module,
__unused_webpack_exports,
__webpack_require__
) => {
- module.exports = __webpack_require__(1653);
+ module.exports = __webpack_require__(9025);
/***/
},
- /***/ 4282: /***/ (
+ /***/ 6236: /***/ (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
@@ -43,7 +26,7 @@
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__(3108);
/* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1__ =
- __webpack_require__(3952);
+ __webpack_require__(3770);
/* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1___default =
/*#__PURE__*/ __webpack_require__.n(
next_head__WEBPACK_IMPORTED_MODULE_1__
@@ -76,13 +59,30 @@
/***/
},
+
+ /***/ 6510: /***/ (
+ __unused_webpack_module,
+ __unused_webpack_exports,
+ __webpack_require__
+ ) => {
+ (window.__NEXT_P = window.__NEXT_P || []).push([
+ "/head",
+ function () {
+ return __webpack_require__(6236);
+ },
+ ]);
+ if (false) {
+ }
+
+ /***/
+ },
},
/******/ (__webpack_require__) => {
// webpackRuntimeModules
/******/ var __webpack_exec__ = (moduleId) =>
__webpack_require__((__webpack_require__.s = moduleId));
/******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
- __webpack_exec__(1548)
+ __webpack_exec__(6510)
);
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ _N_E = __webpack_exports__;Diff for index-HASH.js
@@ -1,24 +1,7 @@
(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
[3332],
{
- /***/ 6376: /***/ (
- __unused_webpack_module,
- __unused_webpack_exports,
- __webpack_require__
- ) => {
- (window.__NEXT_P = window.__NEXT_P || []).push([
- "/",
- function () {
- return __webpack_require__(8460);
- },
- ]);
- if (false) {
- }
-
- /***/
- },
-
- /***/ 8460: /***/ (
+ /***/ 830: /***/ (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
@@ -36,13 +19,30 @@
/***/
},
+
+ /***/ 1938: /***/ (
+ __unused_webpack_module,
+ __unused_webpack_exports,
+ __webpack_require__
+ ) => {
+ (window.__NEXT_P = window.__NEXT_P || []).push([
+ "/",
+ function () {
+ return __webpack_require__(830);
+ },
+ ]);
+ if (false) {
+ }
+
+ /***/
+ },
},
/******/ (__webpack_require__) => {
// webpackRuntimeModules
/******/ var __webpack_exec__ = (moduleId) =>
__webpack_require__((__webpack_require__.s = moduleId));
/******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
- __webpack_exec__(6376)
+ __webpack_exec__(1938)
);
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ _N_E = __webpack_exports__;Diff for link-HASH.js
@@ -1,7 +1,220 @@
(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
[4672],
{
- /***/ 857: /***/ (module, exports, __webpack_require__) => {
+ /***/ 2774: /***/ (module, exports, __webpack_require__) => {
+ "use strict";
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true,
+ });
+ Object.defineProperty(exports, "useIntersection", {
+ enumerable: true,
+ get: function () {
+ return useIntersection;
+ },
+ });
+ const _react = __webpack_require__(4312);
+ const _requestidlecallback = __webpack_require__(9413);
+ const hasIntersectionObserver =
+ typeof IntersectionObserver === "function";
+ const observers = new Map();
+ const idList = [];
+ function createObserver(options) {
+ const id = {
+ root: options.root || null,
+ margin: options.rootMargin || "",
+ };
+ const existing = idList.find(
+ (obj) => obj.root === id.root && obj.margin === id.margin
+ );
+ let instance;
+ if (existing) {
+ instance = observers.get(existing);
+ if (instance) {
+ return instance;
+ }
+ }
+ const elements = new Map();
+ const observer = new IntersectionObserver((entries) => {
+ entries.forEach((entry) => {
+ const callback = elements.get(entry.target);
+ const isVisible =
+ entry.isIntersecting || entry.intersectionRatio > 0;
+ if (callback && isVisible) {
+ callback(isVisible);
+ }
+ });
+ }, options);
+ instance = {
+ id,
+ observer,
+ elements,
+ };
+ idList.push(id);
+ observers.set(id, instance);
+ return instance;
+ }
+ function observe(element, callback, options) {
+ const { id, observer, elements } = createObserver(options);
+ elements.set(element, callback);
+ observer.observe(element);
+ return function unobserve() {
+ elements.delete(element);
+ observer.unobserve(element);
+ // Destroy observer when there's nothing left to watch:
+ if (elements.size === 0) {
+ observer.disconnect();
+ observers.delete(id);
+ const index = idList.findIndex(
+ (obj) => obj.root === id.root && obj.margin === id.margin
+ );
+ if (index > -1) {
+ idList.splice(index, 1);
+ }
+ }
+ };
+ }
+ function useIntersection({ rootRef, rootMargin, disabled }) {
+ const isDisabled = disabled || !hasIntersectionObserver;
+ const [visible, setVisible] = (0, _react.useState)(false);
+ const elementRef = (0, _react.useRef)(null);
+ const setElement = (0, _react.useCallback)((element) => {
+ elementRef.current = element;
+ }, []);
+ (0, _react.useEffect)(() => {
+ if (hasIntersectionObserver) {
+ if (isDisabled || visible) return;
+ const element = elementRef.current;
+ if (element && element.tagName) {
+ const unobserve = observe(
+ element,
+ (isVisible) => isVisible && setVisible(isVisible),
+ {
+ root: rootRef?.current,
+ rootMargin,
+ }
+ );
+ return unobserve;
+ }
+ } else {
+ if (!visible) {
+ const idleCallback = (0,
+ _requestidlecallback.requestIdleCallback)(() => setVisible(true));
+ return () =>
+ (0, _requestidlecallback.cancelIdleCallback)(idleCallback);
+ }
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isDisabled, rootMargin, rootRef, visible, elementRef.current]);
+ const resetVisible = (0, _react.useCallback)(() => {
+ setVisible(false);
+ }, []);
+ return [setElement, visible, resetVisible];
+ }
+ if (
+ (typeof exports.default === "function" ||
+ (typeof exports.default === "object" && exports.default !== null)) &&
+ typeof exports.default.__esModule === "undefined"
+ ) {
+ Object.defineProperty(exports.default, "__esModule", {
+ value: true,
+ });
+ Object.assign(exports.default, exports);
+ module.exports = exports.default;
+ } //# sourceMappingURL=use-intersection.js.map
+
+ /***/
+ },
+
+ /***/ 3351: /***/ (module, exports, __webpack_require__) => {
+ "use strict";
+
+ Object.defineProperty(exports, "__esModule", {
+ value: true,
+ });
+ Object.defineProperty(exports, "useMergedRef", {
+ enumerable: true,
+ get: function () {
+ return useMergedRef;
+ },
+ });
+ const _react = __webpack_require__(4312);
+ function useMergedRef(refA, refB) {
+ const cleanupA = (0, _react.useRef)(null);
+ const cleanupB = (0, _react.useRef)(null);
+ // NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
+ // (this happens often if the user doesn't pass a ref to Link/Form/Image)
+ // But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
+ // and the user might pass that ref into ref-merging library that doesn't support cleanup refs
+ // (because it hasn't been updated for React 19)
+ // which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
+ // So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
+ return (0, _react.useCallback)(
+ (current) => {
+ if (current === null) {
+ const cleanupFnA = cleanupA.current;
+ if (cleanupFnA) {
+ cleanupA.current = null;
+ cleanupFnA();
+ }
+ const cleanupFnB = cleanupB.current;
+ if (cleanupFnB) {
+ cleanupB.current = null;
+ cleanupFnB();
+ }
+ } else {
+ if (refA) {
+ cleanupA.current = applyRef(refA, current);
+ }
+ if (refB) {
+ cleanupB.current = applyRef(refB, current);
+ }
+ }
+ },
+ [refA, refB]
+ );
+ }
+ function applyRef(refA, current) {
+ if (typeof refA === "function") {
+ const cleanup = refA(current);
+ if (typeof cleanup === "function") {
+ return cleanup;
+ } else {
+ return () => refA(null);
+ }
+ } else {
+ refA.current = current;
+ return () => {
+ refA.current = null;
+ };
+ }
+ }
+ if (
+ (typeof exports.default === "function" ||
+ (typeof exports.default === "object" && exports.default !== null)) &&
+ typeof exports.default.__esModule === "undefined"
+ ) {
+ Object.defineProperty(exports.default, "__esModule", {
+ value: true,
+ });
+ Object.assign(exports.default, exports);
+ module.exports = exports.default;
+ } //# sourceMappingURL=use-merged-ref.js.map
+
+ /***/
+ },
+
+ /***/ 3440: /***/ (
+ module,
+ __unused_webpack_exports,
+ __webpack_require__
+ ) => {
+ module.exports = __webpack_require__(4757);
+
+ /***/
+ },
+
+ /***/ 4757: /***/ (module, exports, __webpack_require__) => {
"use strict";
/* __next_internal_client_entry_do_not_use__ cjs */
Object.defineProperty(exports, "__esModule", {
@@ -23,22 +236,22 @@
return useLinkStatus;
},
});
- const _interop_require_wildcard = __webpack_require__(8781);
+ const _interop_require_wildcard = __webpack_require__(1162);
const _jsxruntime = __webpack_require__(3108);
const _react = /*#__PURE__*/ _interop_require_wildcard._(
__webpack_require__(4312)
);
- const _resolvehref = __webpack_require__(4055);
- const _islocalurl = __webpack_require__(7175);
- const _formaturl = __webpack_require__(9674);
- const _utils = __webpack_require__(7424);
- const _addlocale = __webpack_require__(589);
- const _routercontextsharedruntime = __webpack_require__(7010);
- const _useintersection = __webpack_require__(2330);
- const _getdomainlocale = __webpack_require__(7207);
- const _addbasepath = __webpack_require__(9942);
- const _usemergedref = __webpack_require__(8067);
- const _erroronce = __webpack_require__(1945);
+ const _resolvehref = __webpack_require__(4691);
+ const _islocalurl = __webpack_require__(4763);
+ const _formaturl = __webpack_require__(3246);
+ const _utils = __webpack_require__(7372);
+ const _addlocale = __webpack_require__(1809);
+ const _routercontextsharedruntime = __webpack_require__(2254);
+ const _useintersection = __webpack_require__(2774);
+ const _getdomainlocale = __webpack_require__(8803);
+ const _addbasepath = __webpack_require__(5834);
+ const _usemergedref = __webpack_require__(3351);
+ const _erroronce = __webpack_require__(6021);
const prefetched = new Set();
function prefetch(router, href, as, options) {
if (false) {
@@ -417,168 +630,43 @@
/***/
},
- /***/ 1945: /***/ (__unused_webpack_module, exports) => {
- "use strict";
-
- Object.defineProperty(exports, "__esModule", {
- value: true,
- });
- Object.defineProperty(exports, "errorOnce", {
- enumerable: true,
- get: function () {
- return errorOnce;
+ /***/ 5326: /***/ (
+ __unused_webpack_module,
+ __unused_webpack_exports,
+ __webpack_require__
+ ) => {
+ (window.__NEXT_P = window.__NEXT_P || []).push([
+ "/link",
+ function () {
+ return __webpack_require__(9058);
},
- });
- let errorOnce = (_) => {};
+ ]);
if (false) {
- } //# sourceMappingURL=error-once.js.map
+ }
/***/
},
- /***/ 2330: /***/ (module, exports, __webpack_require__) => {
+ /***/ 6021: /***/ (__unused_webpack_module, exports) => {
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true,
});
- Object.defineProperty(exports, "useIntersection", {
+ Object.defineProperty(exports, "errorOnce", {
enumerable: true,
get: function () {
- return useIntersection;
+ return errorOnce;
},
});
- const _react = __webpack_require__(4312);
- const _requestidlecallback = __webpack_require__(601);
- const hasIntersectionObserver =
- typeof IntersectionObserver === "function";
- const observers = new Map();
- const idList = [];
- function createObserver(options) {
- const id = {
- root: options.root || null,
- margin: options.rootMargin || "",
- };
- const existing = idList.find(
- (obj) => obj.root === id.root && obj.margin === id.margin
- );
- let instance;
- if (existing) {
- instance = observers.get(existing);
- if (instance) {
- return instance;
- }
- }
- const elements = new Map();
- const observer = new IntersectionObserver((entries) => {
- entries.forEach((entry) => {
- const callback = elements.get(entry.target);
- const isVisible =
- entry.isIntersecting || entry.intersectionRatio > 0;
- if (callback && isVisible) {
- callback(isVisible);
- }
- });
- }, options);
- instance = {
- id,
- observer,
- elements,
- };
- idList.push(id);
- observers.set(id, instance);
- return instance;
- }
- function observe(element, callback, options) {
- const { id, observer, elements } = createObserver(options);
- elements.set(element, callback);
- observer.observe(element);
- return function unobserve() {
- elements.delete(element);
- observer.unobserve(element);
- // Destroy observer when there's nothing left to watch:
- if (elements.size === 0) {
- observer.disconnect();
- observers.delete(id);
- const index = idList.findIndex(
- (obj) => obj.root === id.root && obj.margin === id.margin
- );
- if (index > -1) {
- idList.splice(index, 1);
- }
- }
- };
- }
- function useIntersection({ rootRef, rootMargin, disabled }) {
- const isDisabled = disabled || !hasIntersectionObserver;
- const [visible, setVisible] = (0, _react.useState)(false);
- const elementRef = (0, _react.useRef)(null);
- const setElement = (0, _react.useCallback)((element) => {
- elementRef.current = element;
- }, []);
- (0, _react.useEffect)(() => {
- if (hasIntersectionObserver) {
- if (isDisabled || visible) return;
- const element = elementRef.current;
- if (element && element.tagName) {
- const unobserve = observe(
- element,
- (isVisible) => isVisible && setVisible(isVisible),
- {
- root: rootRef?.current,
- rootMargin,
- }
- );
- return unobserve;
- }
- } else {
- if (!visible) {
- const idleCallback = (0,
- _requestidlecallback.requestIdleCallback)(() => setVisible(true));
- return () =>
- (0, _requestidlecallback.cancelIdleCallback)(idleCallback);
- }
- }
- // eslint-disable-next-line react-hooks/exhaustive-deps
- }, [isDisabled, rootMargin, rootRef, visible, elementRef.current]);
- const resetVisible = (0, _react.useCallback)(() => {
- setVisible(false);
- }, []);
- return [setElement, visible, resetVisible];
- }
- if (
- (typeof exports.default === "function" ||
- (typeof exports.default === "object" && exports.default !== null)) &&
- typeof exports.default.__esModule === "undefined"
- ) {
- Object.defineProperty(exports.default, "__esModule", {
- value: true,
- });
- Object.assign(exports.default, exports);
- module.exports = exports.default;
- } //# sourceMappingURL=use-intersection.js.map
-
- /***/
- },
-
- /***/ 4972: /***/ (
- __unused_webpack_module,
- __unused_webpack_exports,
- __webpack_require__
- ) => {
- (window.__NEXT_P = window.__NEXT_P || []).push([
- "/link",
- function () {
- return __webpack_require__(7600);
- },
- ]);
+ let errorOnce = (_) => {};
if (false) {
- }
+ } //# sourceMappingURL=error-once.js.map
/***/
},
- /***/ 7207: /***/ (module, exports, __webpack_require__) => {
+ /***/ 8803: /***/ (module, exports, __webpack_require__) => {
"use strict";
Object.defineProperty(exports, "__esModule", {
@@ -590,7 +678,7 @@
return getDomainLocale;
},
});
- const _normalizetrailingslash = __webpack_require__(151);
+ const _normalizetrailingslash = __webpack_require__(1315);
const basePath =
/* unused pure expression or super */ null && (false || "");
function getDomainLocale(path, locale, locales, domainLocales) {
@@ -614,7 +702,7 @@
/***/
},
- /***/ 7600: /***/ (
+ /***/ 9058: /***/ (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
@@ -629,7 +717,7 @@
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__(3108);
/* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1__ =
- __webpack_require__(7830);
+ __webpack_require__(3440);
/* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1___default =
/*#__PURE__*/ __webpack_require__.n(
next_link__WEBPACK_IMPORTED_MODULE_1__
@@ -659,101 +747,13 @@
/***/
},
-
- /***/ 7830: /***/ (
- module,
- __unused_webpack_exports,
- __webpack_require__
- ) => {
- module.exports = __webpack_require__(857);
-
- /***/
- },
-
- /***/ 8067: /***/ (module, exports, __webpack_require__) => {
- "use strict";
-
- Object.defineProperty(exports, "__esModule", {
- value: true,
- });
- Object.defineProperty(exports, "useMergedRef", {
- enumerable: true,
- get: function () {
- return useMergedRef;
- },
- });
- const _react = __webpack_require__(4312);
- function useMergedRef(refA, refB) {
- const cleanupA = (0, _react.useRef)(null);
- const cleanupB = (0, _react.useRef)(null);
- // NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
- // (this happens often if the user doesn't pass a ref to Link/Form/Image)
- // But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
- // and the user might pass that ref into ref-merging library that doesn't support cleanup refs
- // (because it hasn't been updated for React 19)
- // which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
- // So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
- return (0, _react.useCallback)(
- (current) => {
- if (current === null) {
- const cleanupFnA = cleanupA.current;
- if (cleanupFnA) {
- cleanupA.current = null;
- cleanupFnA();
- }
- const cleanupFnB = cleanupB.current;
- if (cleanupFnB) {
- cleanupB.current = null;
- cleanupFnB();
- }
- } else {
- if (refA) {
- cleanupA.current = applyRef(refA, current);
- }
- if (refB) {
- cleanupB.current = applyRef(refB, current);
- }
- }
- },
- [refA, refB]
- );
- }
- function applyRef(refA, current) {
- if (typeof refA === "function") {
- const cleanup = refA(current);
- if (typeof cleanup === "function") {
- return cleanup;
- } else {
- return () => refA(null);
- }
- } else {
- refA.current = current;
- return () => {
- refA.current = null;
- };
- }
- }
- if (
- (typeof exports.default === "function" ||
- (typeof exports.default === "object" && exports.default !== null)) &&
- typeof exports.default.__esModule === "undefined"
- ) {
- Object.defineProperty(exports.default, "__esModule", {
- value: true,
- });
- Object.assign(exports.default, exports);
- module.exports = exports.default;
- } //# sourceMappingURL=use-merged-ref.js.map
-
- /***/
- },
},
/******/ (__webpack_require__) => {
// webpackRuntimeModules
/******/ var __webpack_exec__ = (moduleId) =>
__webpack_require__((__webpack_require__.s = moduleId));
/******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
- __webpack_exec__(4972)
+ __webpack_exec__(5326)
);
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ _N_E = __webpack_exports__;Diff for routerDirect-HASH.js
@@ -1,17 +1,7 @@
(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
[188],
{
- /***/ 1179: /***/ (
- module,
- __unused_webpack_exports,
- __webpack_require__
- ) => {
- module.exports = __webpack_require__(9864);
-
- /***/
- },
-
- /***/ 1640: /***/ (
+ /***/ 2014: /***/ (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
@@ -26,7 +16,7 @@
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__(3108);
/* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1__ =
- __webpack_require__(1179);
+ __webpack_require__(4169);
/* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1___default =
/*#__PURE__*/ __webpack_require__.n(
next_router__WEBPACK_IMPORTED_MODULE_1__
@@ -46,7 +36,17 @@
/***/
},
- /***/ 2172: /***/ (
+ /***/ 4169: /***/ (
+ module,
+ __unused_webpack_exports,
+ __webpack_require__
+ ) => {
+ module.exports = __webpack_require__(9700);
+
+ /***/
+ },
+
+ /***/ 4414: /***/ (
__unused_webpack_module,
__unused_webpack_exports,
__webpack_require__
@@ -54,7 +54,7 @@
(window.__NEXT_P = window.__NEXT_P || []).push([
"/routerDirect",
function () {
- return __webpack_require__(1640);
+ return __webpack_require__(2014);
},
]);
if (false) {
@@ -68,7 +68,7 @@
/******/ var __webpack_exec__ = (moduleId) =>
__webpack_require__((__webpack_require__.s = moduleId));
/******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
- __webpack_exec__(2172)
+ __webpack_exec__(4414)
);
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ _N_E = __webpack_exports__;Diff for script-HASH.js
@@ -1,17 +1,34 @@
(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
[1209],
{
- /***/ 4977: /***/ (
+ /***/ 2591: /***/ (
module,
__unused_webpack_exports,
__webpack_require__
) => {
- module.exports = __webpack_require__(326);
+ module.exports = __webpack_require__(4634);
/***/
},
- /***/ 5887: /***/ (
+ /***/ 5030: /***/ (
+ __unused_webpack_module,
+ __unused_webpack_exports,
+ __webpack_require__
+ ) => {
+ (window.__NEXT_P = window.__NEXT_P || []).push([
+ "/script",
+ function () {
+ return __webpack_require__(7557);
+ },
+ ]);
+ if (false) {
+ }
+
+ /***/
+ },
+
+ /***/ 7557: /***/ (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
@@ -26,7 +43,7 @@
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__(3108);
/* harmony import */ var next_script__WEBPACK_IMPORTED_MODULE_1__ =
- __webpack_require__(4977);
+ __webpack_require__(2591);
/* harmony import */ var next_script__WEBPACK_IMPORTED_MODULE_1___default =
/*#__PURE__*/ __webpack_require__.n(
next_script__WEBPACK_IMPORTED_MODULE_1__
@@ -58,30 +75,13 @@
/***/
},
-
- /***/ 6268: /***/ (
- __unused_webpack_module,
- __unused_webpack_exports,
- __webpack_require__
- ) => {
- (window.__NEXT_P = window.__NEXT_P || []).push([
- "/script",
- function () {
- return __webpack_require__(5887);
- },
- ]);
- if (false) {
- }
-
- /***/
- },
},
/******/ (__webpack_require__) => {
// webpackRuntimeModules
/******/ var __webpack_exec__ = (moduleId) =>
__webpack_require__((__webpack_require__.s = moduleId));
/******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
- __webpack_exec__(6268)
+ __webpack_exec__(5030)
);
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ _N_E = __webpack_exports__;Diff for withRouter-HASH.js
@@ -1,34 +1,7 @@
(self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
[3263],
{
- /***/ 1179: /***/ (
- module,
- __unused_webpack_exports,
- __webpack_require__
- ) => {
- module.exports = __webpack_require__(9864);
-
- /***/
- },
-
- /***/ 2028: /***/ (
- __unused_webpack_module,
- __unused_webpack_exports,
- __webpack_require__
- ) => {
- (window.__NEXT_P = window.__NEXT_P || []).push([
- "/withRouter",
- function () {
- return __webpack_require__(4501);
- },
- ]);
- if (false) {
- }
-
- /***/
- },
-
- /***/ 4501: /***/ (
+ /***/ 559: /***/ (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
@@ -43,7 +16,7 @@
/* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__(3108);
/* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1__ =
- __webpack_require__(1179);
+ __webpack_require__(4169);
/* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1___default =
/*#__PURE__*/ __webpack_require__.n(
next_router__WEBPACK_IMPORTED_MODULE_1__
@@ -61,13 +34,40 @@
/***/
},
+
+ /***/ 726: /***/ (
+ __unused_webpack_module,
+ __unused_webpack_exports,
+ __webpack_require__
+ ) => {
+ (window.__NEXT_P = window.__NEXT_P || []).push([
+ "/withRouter",
+ function () {
+ return __webpack_require__(559);
+ },
+ ]);
+ if (false) {
+ }
+
+ /***/
+ },
+
+ /***/ 4169: /***/ (
+ module,
+ __unused_webpack_exports,
+ __webpack_require__
+ ) => {
+ module.exports = __webpack_require__(9700);
+
+ /***/
+ },
},
/******/ (__webpack_require__) => {
// webpackRuntimeModules
/******/ var __webpack_exec__ = (moduleId) =>
__webpack_require__((__webpack_require__.s = moduleId));
/******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
- __webpack_exec__(2028)
+ __webpack_exec__(726)
);
/******/ var __webpack_exports__ = __webpack_require__.O();
/******/ _N_E = __webpack_exports__;Diff for 2161-HASH.js
Diff too large to display
Diff for 2747-HASH.js
Diff too large to display
Diff for 4322-HASH.js
failed to diffDiff for main-HASH.js
Diff too large to display
.claude-plugin/plugins/cache-components/skills/cache-components/PATTERNS.md
Outdated
Show resolved
Hide resolved
0c20f56 to
19696a8
Compare
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Show resolved
Hide resolved
c3144f0 to
a968dcc
Compare
.claude-plugin/plugins/cache-components/skills/cache-components/TROUBLESHOOTING.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Show resolved
Hide resolved
✅ Tests PassedUpdated 2026-01-05 16:25:17 UTC · Commit: 8511eda |
| (dynamic streaming) | ||
| ``` | ||
|
|
||
| **Key insight**: The `'use cache'` directive is for data that's the _same across users_. User-specific data either uses `'use cache: private'` or stays dynamic with Suspense. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Having a hard time thinking what this should say today, since (a) use cache: private doesn't do anything yet and (b) use cache will also be useful for user-specific data once the cache-before-navs and cache-after-navs features are complete.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I can remove "use cache: private" references for now until we have a clearer picture?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I think so 👍
| | ----------- | ----- | ------------- | -------------- | | ||
| | `'default'` | 300\* | 900 (15min) | ∞ (INFINITE) | | ||
| | `'seconds'` | 30 | 1 | 60 | | ||
| | `'minutes'` | 300 | 60 (1min) | 3600 (1hr) | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 'minutes' profile had stale and revalidate values swapped, violating the caching constraint stale ≤ revalidate.
View Details
📝 Patch Details
diff --git a/.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md b/.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
index bd76cfb719..58aa3dcfc8 100644
--- a/.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
+++ b/.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
@@ -89,7 +89,7 @@ interface CacheLifeOptions {
| ----------- | ----- | ------------- | -------------- |
| `'default'` | 300\* | 900 (15min) | ∞ (INFINITE) |
| `'seconds'` | 30 | 1 | 60 |
-| `'minutes'` | 300 | 60 (1min) | 3600 (1hr) |
+| `'minutes'` | 60 | 300 (5min) | 3600 (1hr) |
| `'hours'` | 300 | 3600 (1hr) | 86400 (1day) |
| `'days'` | 300 | 86400 (1day) | 604800 (1wk) |
| `'weeks'` | 300 | 604800 (1wk) | 2592000 (30d) |
Analysis
The 'minutes' profile in the predefined profiles table had stale=300 and revalidate=60, which violates the HTTP caching semantics constraint that stale ≤ revalidate.
Evidence:
-
The parameter constraint documentation explicitly states
revalidate ≤ expire -
The HTTP Cache-Control mapping shows stale→max-age and revalidate→s-maxage, where client cache (max-age) should not exceed server cache (s-maxage)
-
All other predefined profiles follow the pattern stale ≤ revalidate:
- 'default': 300 ≤ 900 ✓
- 'seconds': 30 ≤ 1 ✓ (exception where stale is larger, but makes sense for very short timescales)
- 'hours': 300 ≤ 3600 ✓
- 'days': 300 ≤ 86400 ✓
- 'weeks': 300 ≤ 604800 ✓
- 'max': 300 ≤ 2592000 ✓
-
Custom profile examples in the same document follow the correct pattern:
- 'blog-posts': stale=300, revalidate=3600 (300 ≤ 3600) ✓
- 'default' override: stale=60, revalidate=600 (60 ≤ 600) ✓
The fix swapped the values: stale=60, revalidate=300, which makes semantic sense for a "minutes" profile (1 minute client cache, 5 minute revalidation window, 1 hour absolute expiration) and aligns with the constraint pattern followed throughout the document.
Tests Passed |
9d3bfcb to
7cc5728
Compare
Stats from current PR✅ No significant changes detected📊 All Metrics📖 Metrics GlossaryDev Server Metrics:
Build Metrics:
Change Thresholds:
⚡ Dev Server
📦 Dev Server (Webpack) (Legacy)📦 Dev Server (Webpack)
⚡ Production Builds
📦 Production Builds (Webpack) (Legacy)📦 Production Builds (Webpack)
📦 Bundle SizesBundle Sizes⚡ TurbopackClient Main Bundles: **431 kB** → **431 kB** ✅ -16 B82 files with content-based hashes (individual files not comparable between builds) Server Middleware
Build DetailsBuild Manifests
📦 WebpackClient Main Bundles
Polyfills
Pages
Server Edge SSR
Middleware
Build DetailsBuild Manifests
Build Cache
🔄 Shared (bundler-independent)Runtimes
|
This stack of pull requests is managed by Graphite. Learn more about stacking. |
samselikoff
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just got through the REFERENCE.md file so far, will get to the others soon!
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Show resolved
Hide resolved
|
|
||
| \* Default `stale` falls back to `experimental.staleTimes.static` (300 seconds) | ||
|
|
||
| > **Important:** Profiles with `expire < 300` seconds (like `'seconds'`) are treated as **dynamic** and won't be included in the static shell during Partial Prerendering. See [Dynamic Threshold](#dynamic-threshold) below. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I thought it stale < 30 was the threshold here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The threshold is expire < 300 (5 minutes), not stale < 30. Here's the code evidence:
Constants defined in packages/next/src/server/use-cache/constants.ts:
export const DYNAMIC_EXPIRE = 300 // 5 minutes
export const RUNTIME_PREFETCH_DYNAMIC_STALE = 30 // 30 secondsNote: RUNTIME_PREFETCH_DYNAMIC_STALE = 30 exists but is used for prefetch stale time, not the dynamic threshold.
Dynamic threshold check in packages/next/src/server/use-cache/use-cache-wrapper.ts (lines 1246-1255):
if (
existingEntry.revalidate === 0 ||
existingEntry.expire < DYNAMIC_EXPIRE // <-- expire, not stale
) {
switch (workUnitStore.type) {
case 'prerender':
// In a Dynamic I/O prerender, if the cache entry has
// revalidate: 0 or if the expire time is under 5 minutes,
// then we consider this cache entry dynamic as it's not worth
// generating static pages for such data.Also used in packages/next/src/server/resume-data-cache/cache-store.ts (line 105):
if (
isCacheComponentsEnabled &&
(entry.revalidate === 0 || entry.expire < DYNAMIC_EXPIRE)
) {
// The entry was omitted from the prerender result
return null
}So the dynamic threshold conditions are:
revalidate === 0→ treated as dynamicexpire < 300seconds → treated as dynamic
The stale parameter doesn't affect this decision - it only controls client-side freshness perception.
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/REFERENCE.md
Show resolved
Hide resolved
7cc5728 to
0633b07
Compare
samselikoff
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
PATTERNS.md
.claude-plugin/plugins/cache-components/skills/cache-components/PATTERNS.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/PATTERNS.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/PATTERNS.md
Outdated
Show resolved
Hide resolved
.claude-plugin/plugins/cache-components/skills/cache-components/PATTERNS.md
Show resolved
Hide resolved
ff74096 to
ef023e7
Compare
Introduces a Claude Code plugin marketplace in the Next.js repository, enabling team members to install development assistance plugins directly from the repo. Initial plugin: cache-components - Expert guidance for Cache Components and Partial Prerendering (PPR) - Proactively activates in projects with `cacheComponents: true` - Covers 'use cache' directive, cacheLife(), cacheTag(), invalidation APIs - Explains parameter permutation rendering and subshell generation - Includes migration guide from deprecated revalidate/dynamic configs - Provides build-time error solutions and troubleshooting Installation: /plugin marketplace add vercel/next.js /plugin install cache-components@nextjs
Add mental model decision tree, cache scope clarification, updateTag vs revalidateTag guide, migration scenarios, and debugging checklist. Fix 'use server' directive syntax and params Promise type in code examples.
… code review - Fix cache life profiles table with accurate values from config-shared.ts - 'default' expire is INFINITE_CACHE, not 3600 - 'minutes' revalidate is 60, not 300 - Most profiles use stale=300, not escalating values - 'max' revalidate is 2592000 (30d), not 31536000 - Add DYNAMIC_EXPIRE threshold documentation (expire < 300s = dynamic) - Add tag constraints section (max 256 chars, max 128 tags) - Add implicit tags documentation (_N_T_ prefix, revalidatePath behavior) - Document revalidateTag optional profile parameter for stale-while-revalidate - Fix object arguments explanation (structural serialization, not referential) - Add cache key composition details (buildId, functionId, hmrRefreshHash) - Add Runtime Behaviors section: - Draft mode cache bypass behavior - Cache bypass conditions table - 50-second prerender timeout details - HMR cache invalidation in development - Cache propagation for nested caches - Fix deprecated pattern inconsistency in TROUBLESHOOTING.md - Remove 'force-dynamic' suggestion - Add Suspense-based alternative with deprecation note
Prettier incorrectly parses multi-file code examples within markdown
code blocks as single JavaScript files. When it sees 'use server' at
the start of what appears to be a new "file" (denoted by a comment
like `// actions/posts.ts`), it interprets it as an orphan string
expression and "fixes" it by adding a semicolon prefix: `;('use server')`.
This is semantically incorrect for documentation examples.
The documentation incorrectly stated that `stale ≤ revalidate` was a constraint. Looking at the actual validation in cache-life.ts, the only enforced constraint is `revalidate ≤ expire`. The 'seconds' profile intentionally has stale=30 > revalidate=1 to allow brief client caching while keeping the server cache fresh.
- Add comparison table for 'use cache: private' vs parameter extraction - Add decision table for implicit vs explicit cache tags - Add cross-references from TROUBLESHOOTING errors to PATTERNS solutions - Add note about synchronous databases (e.g., better-sqlite3) behavior
Remove documentation for 'use cache: private' directive as the feature picture is not yet complete enough to recommend its usage.
- Fix revalidateTag signature to show correct type: `{ expire?: number }`
instead of CacheLifeOptions (which has stale/revalidate/expire)
- Add note explaining difference between cacheLife() and revalidateTag()
object parameter types
- Add required second argument to all revalidateTag() examples
- Fix revalidateTag signature to show correct type: `{ expire?: number }`
instead of CacheLifeOptions (which has stale/revalidate/expire)
- Add note explaining difference between cacheLife() and revalidateTag()
object parameter types
- Add required second argument to all revalidateTag() examples
ef023e7 to
f241bcb
Compare
…mponents Add comprehensive guidance on Suspense usage with cached components: - Expand inline comment in Pattern 9 explaining build vs runtime behavior - Add new "When to Use Suspense with Cached Components" section with tables - Update anti-pattern to distinguish dynamic (required) vs cached (optional) Addresses reviewer question about whether cached components need Suspense.
Remove "Parameter Extraction for Runtime Data" pattern and related examples that only work with runtime prefetching (unstable). Changes: - Remove Pattern 2 from PATTERNS.md, renumber remaining patterns - Remove "Handling Runtime Data" section from SKILL.md - Update TROUBLESHOOTING.md to recommend not caching user-specific content instead of passing runtime data as arguments This avoids confusing users into thinking the pattern works today.
| "source": { | ||
| "source": "github", | ||
| "repo": "vercel/next.js" | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nested source key appears to be a typo. The configuration has "source": { "source": "github", ... } which contains a duplicate source property. This likely should be:
"source": {
"type": "github",
"repo": "vercel/next.js"
}Or the inner object structure may be incorrect. Having the same key name nested inside itself is almost certainly unintentional and will cause the configuration to fail when users try to follow this example.
| "source": { | |
| "source": "github", | |
| "repo": "vercel/next.js" | |
| } | |
| "source": { | |
| "type": "github", | |
| "repo": "vercel/next.js" | |
| } |
Spotted by Graphite Agent
Is this helpful? React 👍 or 👎 to let us know.
…ercel#87993) ### What? Adds a Claude Code plugin marketplace to the Next.js repository with an initial `cache-components` plugin that provides expert guidance for Cache Components and Partial Prerendering (PPR). ### Why? Cache Components introduces a new programming model that combines Partial Prerendering, Dynamic I/O, and the `"use cache"` directive. Team members and contributors need guidance on: - The `'use cache'` directive and its variants (`private`, `remote`) - Cache lifetime configuration with `cacheLife()` and predefined profiles - Cache tagging and invalidation with `cacheTag()`, `updateTag()`, `revalidateTag()` - Parameter permutation rendering and subshell generation with `generateStaticParams` - Migration from deprecated `revalidate` and `dynamic` segment configs - Build-time error resolution (dynamic data outside Suspense, uncached data, etc.) A Claude Code plugin allows this knowledge to be distributed directly from the repo and proactively activated when working in projects with `cacheComponents: true`. ### How? Introduces the `.claude-plugin/` directory structure: ``` .claude-plugin/ ├── marketplace.json ← Marketplace catalog (name: "nextjs") └── plugins/ ├── README.md ← Documentation for adding plugins └── cache-components/ ├── .claude-plugin/plugin.json ← Plugin manifest ├── README.md ← Plugin documentation └── skills/cache-components/ ├── SKILL.md ← Core concepts, mental model (496 lines) ├── REFERENCE.md ← API reference, migrations (875 lines) ├── PATTERNS.md ← 12 production patterns (781 lines) └── TROUBLESHOOTING.md ← Build errors, debugging (721 lines) ``` **Installation:** ```bash /plugin marketplace add vercel/next.js /plugin install cache-components@nextjs ``` **Key skill features:** - **Proactive activation** when `cacheComponents: true` is detected in next.config - **Mental model decision tree** for thinking through caching decisions - **Cache scope clarification** explaining what creates new cache entries - **updateTag vs revalidateTag guide** with decision table and examples - **Migration scenarios** from deprecated `revalidate`/`dynamic` segment configs - **Quick debugging checklist** for common cache issues - **Parameter permutation rendering** (how `generateStaticParams` creates subshells) - **12 production patterns** including e-commerce, multi-tenant SaaS, and subshell composition ### Documentation Highlights | Section | Description | |---------|-------------| | Mental Model | Decision tree for when to use `'use cache'`, `'use cache: private'`, or `<Suspense>` | | Cache Scope | What creates new cache entries (function identity, arguments, file path) | | updateTag vs revalidateTag | Decision guide with e-commerce examples | | Migration Scenarios | Before/after examples for `revalidate`, `force-dynamic`, and ISR patterns | | Quick Debugging Checklist | Copy-paste checklists for common issues |

What?
Adds a Claude Code plugin marketplace to the Next.js repository with an initial
cache-componentsplugin that provides expert guidance for Cache Components and Partial Prerendering (PPR).Why?
Cache Components introduces a new programming model that combines Partial Prerendering, Dynamic I/O, and the
"use cache"directive. Team members and contributors need guidance on:'use cache'directive and its variants (private,remote)cacheLife()and predefined profilescacheTag(),updateTag(),revalidateTag()generateStaticParamsrevalidateanddynamicsegment configsA Claude Code plugin allows this knowledge to be distributed directly from the repo and proactively activated when working in projects with
cacheComponents: true.How?
Introduces the
.claude-plugin/directory structure:Installation:
Key skill features:
cacheComponents: trueis detected in next.configrevalidate/dynamicsegment configsgenerateStaticParamscreates subshells)Documentation Highlights
'use cache','use cache: private', or<Suspense>revalidate,force-dynamic, and ISR patternsNAR-702