@decocms/blocks 7.66.6 → 7.66.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/blocks",
3
- "version": "7.66.6",
3
+ "version": "7.66.7",
4
4
  "type": "module",
5
5
  "engines": {
6
6
  "node": ">=24"
@@ -222,6 +222,79 @@ describe("findPageByPath specificity", () => {
222
222
  });
223
223
  });
224
224
 
225
+ describe("getAllPages — page blocks without the `pages-` prefix", () => {
226
+ afterEach(() => {
227
+ setBlocks({});
228
+ });
229
+
230
+ it("routes a page block whose key lacks the prefix", () => {
231
+ // Decofiles carried over from Deco on Fresh hold page blocks under
232
+ // arbitrary keys: renaming a page in the CMS renames its snapshot file,
233
+ // and the key follows the filename. Fresh enumerated pages by
234
+ // __resolveType, so these routed fine there.
235
+ setBlocks({
236
+ "plp-verao": {
237
+ name: "Verão",
238
+ path: "/verao",
239
+ sections: [],
240
+ __resolveType: "website/pages/Page.tsx",
241
+ },
242
+ });
243
+
244
+ const match = findPageByPath("/verao");
245
+ expect(match?.blockKey).toBe("plp-verao");
246
+ });
247
+
248
+ it("still ranks by path specificity across both key shapes", () => {
249
+ setBlocks({
250
+ "pages-catch-all": {
251
+ name: "Catch all",
252
+ path: "/*",
253
+ sections: [],
254
+ },
255
+ "plp-inverno": {
256
+ name: "Inverno",
257
+ path: "/inverno",
258
+ sections: [],
259
+ __resolveType: "website/pages/Page.tsx",
260
+ },
261
+ });
262
+
263
+ // The literal must win over the catch-all regardless of which one
264
+ // carries the prefix.
265
+ expect(findPageByPath("/inverno")?.blockKey).toBe("plp-inverno");
266
+ expect(findPageByPath("/qualquer-outra")?.blockKey).toBe("pages-catch-all");
267
+ });
268
+
269
+ it("ignores non-page blocks that happen to carry a path", () => {
270
+ // A loader block is not a page: no __resolveType match, no prefix.
271
+ setBlocks({
272
+ "some-loader": {
273
+ name: "Not a page",
274
+ path: "/not-a-page",
275
+ sections: [],
276
+ __resolveType: "site/loaders/whatever.ts",
277
+ },
278
+ });
279
+
280
+ expect(findPageByPath("/not-a-page")).toBeNull();
281
+ });
282
+
283
+ it("keeps honouring the prefix when there is no __resolveType", () => {
284
+ // The admin emits prefixed keys and does not always write a
285
+ // __resolveType onto the snapshot — the fast path must still apply.
286
+ setBlocks({
287
+ "pages-sem-tipo": {
288
+ name: "Sem tipo",
289
+ path: "/sem-tipo",
290
+ sections: [],
291
+ },
292
+ });
293
+
294
+ expect(findPageByPath("/sem-tipo")?.blockKey).toBe("pages-sem-tipo");
295
+ });
296
+ });
297
+
225
298
  describe("loadBlocks draft override — key percent-encoding", () => {
226
299
  // The published decofile encodes special characters in block keys
227
300
  // (`pages-Home%20(principal)-1`); the Studio draft emits them raw
package/src/cms/loader.ts CHANGED
@@ -282,6 +282,36 @@ function pathSpecificityKey(path: string): [number, number, number] {
282
282
  return [hasWildcard ? 0 : 1, literals, params];
283
283
  }
284
284
 
285
+ // Literal rather than WELL_KNOWN_TYPES.PAGE: resolve.ts already imports from
286
+ // this module, so importing back would close a cycle.
287
+ const PAGE_RESOLVE_TYPE = "website/pages/Page.tsx";
288
+
289
+ /**
290
+ * Is this block a page?
291
+ *
292
+ * The `pages-` prefix is kept as a fast path because it is what the admin
293
+ * emits and it short-circuits the vast majority of blocks without a property
294
+ * read. But it cannot be the *only* test: a decofile carried over from Deco on
295
+ * Fresh can hold page blocks under any key — renaming one in the CMS renames
296
+ * its snapshot file, and `loadDecofileDirectory` preserves whatever key the
297
+ * filename already had.
298
+ *
299
+ * Those blocks are pages in every way that matters (`website/pages/Page.tsx`,
300
+ * a `path`, `sections`), and on Fresh they routed fine: `website/loaders/pages.ts`
301
+ * enumerated by `__resolveType`, not by key. Filtering on the prefix alone makes
302
+ * them invisible, so every one of their URLs falls through to whatever catch-all
303
+ * the site has — usually a 404 — and they disappear from the CMS sitemap too.
304
+ * One storefront in this state has 205 such blocks against 107 prefixed ones.
305
+ */
306
+ function isPageBlock(key: string, block: unknown): boolean {
307
+ if (key.startsWith("pages-")) return true;
308
+ return (
309
+ typeof block === "object" &&
310
+ block !== null &&
311
+ (block as Resolvable).__resolveType === PAGE_RESOLVE_TYPE
312
+ );
313
+ }
314
+
285
315
  export function getAllPages(): Array<{ key: string; page: DecoPage }> {
286
316
  const blocks = loadBlocks();
287
317
  const pages: Array<{
@@ -291,7 +321,7 @@ export function getAllPages(): Array<{ key: string; page: DecoPage }> {
291
321
  }> = [];
292
322
 
293
323
  for (const [key, block] of Object.entries(blocks)) {
294
- if (!key.startsWith("pages-")) continue;
324
+ if (!isPageBlock(key, block)) continue;
295
325
  const page = block as DecoPage;
296
326
  if (!page.sections) continue;
297
327
  if (!page.path) continue;