@decocms/start 2.24.0 → 2.25.0
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.
|
@@ -430,6 +430,7 @@ logic, wrapped in something else) are skipped automatically.
|
|
|
430
430
|
| `src/sdk/clx.ts` | `@decocms/start/sdk/clx` | yes | Identical implementation; baggagio's extra `clsx` alias has zero callers. |
|
|
431
431
|
| `src/sdk/useSendEvent.ts` | `@decocms/start/sdk/analytics` | no | Site copy uses `<E extends AnalyticsEvent>` generic; framework export is permissive. Replace 1:1 = type-safety loss. Either widen the framework first or accept the loss. |
|
|
432
432
|
| `src/matchers/location.ts` | `@decocms/start/matchers/builtins` | no | Framework's `registerBuiltinMatchers()` ships a richer location matcher (`request.cf` first, geo cookies fallback, headers fallback) plus 10 sibling matchers. Adopting changes behaviour — verify country-name lookup parity, swap `setup.ts`'s `customMatchers` entry. |
|
|
433
|
+
| `src/sdk/url.ts` | `@decocms/apps/commerce/sdk/url` | no | Site fork carries a positional `removeIdSku?: boolean` flag with hardcoded VTEX-specific keys. Canonical apps export uses `{ stripSearchParams: string[] }` (`@decocms/apps@1.9+`). Rewrite imports + each `relative(url, true)` call site → `relative(url, { stripSearchParams: ["idsku", "skuId"] })`, then delete the file. Auto-fix is gated because the call-site rewrite needs JSX/TS-aware transformation, not pure import rewrite. |
|
|
433
434
|
|
|
434
435
|
### Adding a new entry
|
|
435
436
|
|
|
@@ -1215,9 +1215,76 @@ copy-paste regression on any future site gets caught automatically.
|
|
|
1215
1215
|
- **15-B-4** — `Picture` API unification (breaking; needs a
|
|
1216
1216
|
picking-the-winner pass between casaevideo's and baggagio's
|
|
1217
1217
|
shapes, plus a codemod for call sites).
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1218
|
+
|
|
1219
|
+
### Wave 15-B-5 (canonical `relative()` + audit registry entry — apps + deco-start) — 🟡 **IN FLIGHT**
|
|
1220
|
+
|
|
1221
|
+
The smallest 15-B slice: extend `commerce/sdk/url.ts → relative()`
|
|
1222
|
+
with a generic options bag, then point the audit at the canonical
|
|
1223
|
+
so future site forks get caught automatically.
|
|
1224
|
+
|
|
1225
|
+
Verified state (2026-05-01 grep against baggagio-tanstack):
|
|
1226
|
+
|
|
1227
|
+
- `src/sdk/url.ts` carries a positional 2-arg fork (`relative(link,
|
|
1228
|
+
removeIdSku?: boolean)`) with VTEX-specific keys (`idsku`,
|
|
1229
|
+
`skuId`) hardcoded inside.
|
|
1230
|
+
- 9 importers in baggagio. ONE of them — `ProductCard.tsx` — uses
|
|
1231
|
+
the 2-arg form (via prop `removeIdSkuFromUrl`). The other 8 use
|
|
1232
|
+
the 1-arg form, identical to the apps canonical.
|
|
1233
|
+
- casaevideo doesn't carry a fork.
|
|
1234
|
+
|
|
1235
|
+
So the convergence is one apps-side extension + one audit registry
|
|
1236
|
+
entry. The single `ProductCard` call site rewrites by hand or by a
|
|
1237
|
+
future codemod (out of scope here).
|
|
1238
|
+
|
|
1239
|
+
**Shipped (two PRs):**
|
|
1240
|
+
|
|
1241
|
+
39. [`apps-start#36`](https://github.com/decocms/apps-start/pull/36) — `feat(commerce/sdk): extend relative() with stripSearchParams option` ✅ **MERGED** (will release as `@decocms/apps@1.9.x`).
|
|
1242
|
+
- **`commerce/sdk/url.ts`**: backwards-compatible second
|
|
1243
|
+
`RelativeOptions` argument with `stripSearchParams?:
|
|
1244
|
+
string[]` primitive. 1-arg callers (everyone in apps + 8/9
|
|
1245
|
+
of baggagio's call sites) unaffected. The byte-for-byte
|
|
1246
|
+
"://path-style" passthrough is locked in by an explicit
|
|
1247
|
+
backwards-compat test.
|
|
1248
|
+
- **Why generic, not `removeIdSku?: boolean`**: hardcoded VTEX
|
|
1249
|
+
key names belong at call sites, not in a generic commerce
|
|
1250
|
+
helper. `stripSearchParams: string[]` works for any platform.
|
|
1251
|
+
Sites pass `["idsku", "skuId"]` themselves — honest about
|
|
1252
|
+
where the platform knowledge lives.
|
|
1253
|
+
- **`commerce/__tests__/url.test.ts`** (new): 18 tests covering
|
|
1254
|
+
base behaviour (relative/absolute/undefined/empty/malformed
|
|
1255
|
+
via `toString()` thrower), `stripSearchParams` primitive
|
|
1256
|
+
(single, multi, empty, missing, repeated keys, all-stripped
|
|
1257
|
+
→ drop trailing `?`), and three explicit backwards-compat
|
|
1258
|
+
assertions.
|
|
1259
|
+
- 290/290 tests pass, typecheck + biome clean.
|
|
1260
|
+
|
|
1261
|
+
40. `feat(migrate): add url-relative entry to local-framework-duplicate registry` 🟡 **WAITING ON CI** (deco-start side).
|
|
1262
|
+
- **Registry entry** in `FRAMEWORK_DUPLICATES` for
|
|
1263
|
+
`src/sdk/url.ts` → `@decocms/apps/commerce/sdk/url`. Content
|
|
1264
|
+
signature anchored on the legacy positional `removeIdSku?:
|
|
1265
|
+
boolean` shape so sites that already adopted the canonical
|
|
1266
|
+
options-object aren't flagged.
|
|
1267
|
+
- **`safeToAutoFix: false`** — the call-site rewrite from
|
|
1268
|
+
positional `relative(url, true)` to `relative(url, {
|
|
1269
|
+
stripSearchParams: ["idsku", "skuId"] })` requires JSX/TS-
|
|
1270
|
+
aware transformation, not pure import rewrite. The finding's
|
|
1271
|
+
`fix:` field carries the exact recipe.
|
|
1272
|
+
- **Two new tests**: positive case (legacy fork → flagged with
|
|
1273
|
+
the correct hint), negative case (canonical-shaped local
|
|
1274
|
+
fork that already adopted the options object → NOT flagged,
|
|
1275
|
+
proves the signature-anchoring works).
|
|
1276
|
+
- **Skill doc § 8 table** updated with the 4th entry, including
|
|
1277
|
+
version pin (`@decocms/apps@1.9+`).
|
|
1278
|
+
- 355/355 tests pass, typecheck clean. Smoke against baggagio
|
|
1279
|
+
now fires 3 findings (was 2): clx, useSendEvent, url; smoke
|
|
1280
|
+
against casaevideo unchanged at 1 (location-matcher).
|
|
1281
|
+
|
|
1282
|
+
**Process note**: this is the first time we ran the apps-side and
|
|
1283
|
+
deco-start-side as a pair of PRs. The order matters — apps-start
|
|
1284
|
+
must merge first so the deco-start audit registry can point at
|
|
1285
|
+
the released canonical. The skill doc explicitly version-pins the
|
|
1286
|
+
canonical (`@decocms/apps@1.9+`) so engineers reading the audit
|
|
1287
|
+
output know whether they need to bump apps before adopting.
|
|
1221
1288
|
|
|
1222
1289
|
### Wave 15+ (htmx cleanup PRs on als + propagation to other sites) — Priority 3 / 4
|
|
1223
1290
|
|
package/package.json
CHANGED
|
@@ -1053,6 +1053,31 @@ export const FRAMEWORK_DUPLICATES: FrameworkDuplicate[] = [
|
|
|
1053
1053
|
description:
|
|
1054
1054
|
"src/matchers/location.ts overlaps with @decocms/start/matchers/builtins → registerBuiltinMatchers()",
|
|
1055
1055
|
},
|
|
1056
|
+
{
|
|
1057
|
+
id: "url-relative",
|
|
1058
|
+
sitePath: "src/sdk/url.ts",
|
|
1059
|
+
canonicalImport: "@decocms/apps/commerce/sdk/url",
|
|
1060
|
+
// Fingerprint: site fork carries a positional `removeIdSku?: boolean`
|
|
1061
|
+
// flag + hardcoded VTEX-specific keys (`idsku`, `skuId`). Canonical
|
|
1062
|
+
// apps export uses an options object — `{ stripSearchParams: string[] }`
|
|
1063
|
+
// — which is generic and platform-agnostic.
|
|
1064
|
+
contentSignature: [
|
|
1065
|
+
/export\s+const\s+relative\s*=/,
|
|
1066
|
+
/removeIdSku\s*\?\s*:\s*boolean/,
|
|
1067
|
+
/['"](idsku|skuId)['"]/,
|
|
1068
|
+
],
|
|
1069
|
+
safeToAutoFix: false,
|
|
1070
|
+
reason:
|
|
1071
|
+
"rewrite imports to '@decocms/apps/commerce/sdk/url'. " +
|
|
1072
|
+
"Each call site using the boolean form `relative(url, true)` becomes " +
|
|
1073
|
+
"`relative(url, { stripSearchParams: [\"idsku\", \"skuId\"] })`. " +
|
|
1074
|
+
"1-arg calls are unchanged. Then delete src/sdk/url.ts. " +
|
|
1075
|
+
"Auto-fix is gated because the call-site rewrite needs JSX/TS-aware " +
|
|
1076
|
+
"transformation (positional bool → options object), not pure import " +
|
|
1077
|
+
"rewrite.",
|
|
1078
|
+
description:
|
|
1079
|
+
"src/sdk/url.ts overlaps with @decocms/apps/commerce/sdk/url → relative() (extended in @decocms/apps@1.9+)",
|
|
1080
|
+
},
|
|
1056
1081
|
];
|
|
1057
1082
|
|
|
1058
1083
|
const ruleLocalFrameworkDuplicate: Rule = {
|
|
@@ -1240,6 +1240,51 @@ describe("rule: local-framework-duplicate", () => {
|
|
|
1240
1240
|
expect(r.findings[0].fix).toContain("typed AnalyticsEvent generic");
|
|
1241
1241
|
});
|
|
1242
1242
|
|
|
1243
|
+
it("flags src/sdk/url.ts as warn-only (positional bool → options object call-site rewrite)", () => {
|
|
1244
|
+
const fs = makeFs({
|
|
1245
|
+
"/site/src/sdk/url.ts":
|
|
1246
|
+
"export const relative = (\n" +
|
|
1247
|
+
" link?: string | undefined,\n" +
|
|
1248
|
+
" removeIdSku?: boolean,\n" +
|
|
1249
|
+
") => {\n" +
|
|
1250
|
+
' const linkUrl = link ? new URL(link, "http://localhost") : undefined;\n' +
|
|
1251
|
+
" if (linkUrl && removeIdSku) {\n" +
|
|
1252
|
+
' linkUrl.searchParams.delete("idsku");\n' +
|
|
1253
|
+
' linkUrl.searchParams.delete("skuId");\n' +
|
|
1254
|
+
" }\n" +
|
|
1255
|
+
" return linkUrl ? `${linkUrl.pathname}` : undefined;\n" +
|
|
1256
|
+
"};\n",
|
|
1257
|
+
});
|
|
1258
|
+
const report = runAudit(SITE, fs);
|
|
1259
|
+
const r = report.rules.find((r) => r.rule === "local-framework-duplicate")!;
|
|
1260
|
+
expect(r.findings).toHaveLength(1);
|
|
1261
|
+
expect(r.findings[0].file).toBe("src/sdk/url.ts");
|
|
1262
|
+
expect(r.findings[0].meta?.id).toBe("url-relative");
|
|
1263
|
+
expect(r.findings[0].meta?.safeToAutoFix).toBe(false);
|
|
1264
|
+
expect(r.findings[0].meta?.canonicalImport).toBe(
|
|
1265
|
+
"@decocms/apps/commerce/sdk/url",
|
|
1266
|
+
);
|
|
1267
|
+
expect(r.findings[0].fix).toContain("stripSearchParams");
|
|
1268
|
+
});
|
|
1269
|
+
|
|
1270
|
+
it("does NOT flag a forked url.ts that no longer carries the removeIdSku flag", () => {
|
|
1271
|
+
// A site that already adopted an options-object-shaped local helper
|
|
1272
|
+
// should not be flagged — the rule's signature is anchored on the
|
|
1273
|
+
// legacy positional-boolean shape.
|
|
1274
|
+
const fs = makeFs({
|
|
1275
|
+
"/site/src/sdk/url.ts":
|
|
1276
|
+
"export const relative = (link?: string, options?: { stripSearchParams?: string[] }) => {\n" +
|
|
1277
|
+
' if (!link) return undefined;\n' +
|
|
1278
|
+
' return new URL(link, "https://localhost").pathname;\n' +
|
|
1279
|
+
"};\n",
|
|
1280
|
+
});
|
|
1281
|
+
const report = runAudit(SITE, fs);
|
|
1282
|
+
const r = report.rules.find((r) => r.rule === "local-framework-duplicate")!;
|
|
1283
|
+
// url-relative entry must NOT fire on the canonical-shaped local fork.
|
|
1284
|
+
const urlFinding = r.findings.find((f) => f.meta?.id === "url-relative");
|
|
1285
|
+
expect(urlFinding).toBeUndefined();
|
|
1286
|
+
});
|
|
1287
|
+
|
|
1243
1288
|
it("flags src/matchers/location.ts as warn-only (behaviour-superset opportunity)", () => {
|
|
1244
1289
|
const fs = makeFs({
|
|
1245
1290
|
"/site/src/matchers/location.ts":
|