@decocms/start 2.26.0 → 2.28.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.
- package/.agents/skills/deco-to-tanstack-migration/references/css-styling.md +82 -0
- package/.agents/skills/deco-to-tanstack-migration/references/gotchas.md +32 -1
- package/.agents/skills/deco-to-tanstack-migration/templates/setup-ts.md +33 -6
- package/MIGRATION_TOOLING_PLAN.md +176 -0
- package/package.json +6 -1
- package/scripts/migrate/templates/section-loaders.ts +36 -24
- package/src/cms/index.ts +7 -1
- package/src/cms/sectionMixins.test.ts +117 -0
- package/src/cms/sectionMixins.ts +55 -0
- package/src/sdk/cn.test.ts +34 -0
- package/src/sdk/cn.ts +28 -0
- package/src/sdk/cookie.test.ts +108 -0
- package/src/sdk/cookie.ts +90 -0
- package/src/sdk/encoding.test.ts +71 -0
- package/src/sdk/encoding.ts +47 -0
- package/src/sdk/http.test.ts +71 -0
- package/src/sdk/http.ts +124 -0
- package/src/sdk/useScript.test.ts +77 -2
- package/src/sdk/useScript.ts +48 -8
|
@@ -1,5 +1,11 @@
|
|
|
1
|
-
import { describe, expect, it } from "vitest";
|
|
2
|
-
import {
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
|
2
|
+
import {
|
|
3
|
+
HTMX_LEGACY_URL,
|
|
4
|
+
inlineScript,
|
|
5
|
+
usePartialSection,
|
|
6
|
+
useScript,
|
|
7
|
+
useSection,
|
|
8
|
+
} from "./useScript";
|
|
3
9
|
|
|
4
10
|
describe("inlineScript", () => {
|
|
5
11
|
it("returns dangerouslySetInnerHTML with the provided string", () => {
|
|
@@ -51,3 +57,72 @@ describe("useScript", () => {
|
|
|
51
57
|
expect(result).toMatch(/^\(.*\)\(\)$/);
|
|
52
58
|
});
|
|
53
59
|
});
|
|
60
|
+
|
|
61
|
+
// ---------------------------------------------------------------------------
|
|
62
|
+
// Legacy HTMX stubs — useSection / usePartialSection
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
describe("useSection / usePartialSection (legacy HTMX stubs)", () => {
|
|
66
|
+
let warnSpy: ReturnType<typeof vi.spyOn>;
|
|
67
|
+
let prevNodeEnv: string | undefined;
|
|
68
|
+
|
|
69
|
+
beforeEach(() => {
|
|
70
|
+
// Reset the dedup set so each test sees a fresh warning fire.
|
|
71
|
+
delete (globalThis as any).__DECO_LEGACY_HTMX_WARNED;
|
|
72
|
+
prevNodeEnv = process.env.NODE_ENV;
|
|
73
|
+
process.env.NODE_ENV = "development";
|
|
74
|
+
warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
afterEach(() => {
|
|
78
|
+
warnSpy.mockRestore();
|
|
79
|
+
if (prevNodeEnv === undefined) delete process.env.NODE_ENV;
|
|
80
|
+
else process.env.NODE_ENV = prevNodeEnv;
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
it("useSection returns a stable placeholder URL instead of throwing", () => {
|
|
84
|
+
expect(useSection()).toBe(HTMX_LEGACY_URL);
|
|
85
|
+
expect(useSection({ props: { x: 1 } })).toBe(HTMX_LEGACY_URL);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it("usePartialSection returns the same placeholder URL", () => {
|
|
89
|
+
expect(usePartialSection()).toBe(HTMX_LEGACY_URL);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it("warns on first call (in development)", () => {
|
|
93
|
+
useSection();
|
|
94
|
+
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
95
|
+
expect(warnSpy.mock.calls[0][0]).toMatch(/useSection/);
|
|
96
|
+
expect(warnSpy.mock.calls[0][0]).toMatch(/were removed/);
|
|
97
|
+
expect(warnSpy.mock.calls[0][0]).toMatch(/htmx-residue/);
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
it("dedups warnings: a second call to the same stub is silent", () => {
|
|
101
|
+
useSection();
|
|
102
|
+
useSection();
|
|
103
|
+
useSection();
|
|
104
|
+
expect(warnSpy).toHaveBeenCalledTimes(1);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
it("tracks warnings per-stub: useSection and usePartialSection warn independently", () => {
|
|
108
|
+
useSection();
|
|
109
|
+
usePartialSection();
|
|
110
|
+
expect(warnSpy).toHaveBeenCalledTimes(2);
|
|
111
|
+
const messages = warnSpy.mock.calls.map((c) => c[0] as string);
|
|
112
|
+
expect(messages.some((m) => m.includes("useSection"))).toBe(true);
|
|
113
|
+
expect(messages.some((m) => m.includes("usePartialSection"))).toBe(true);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("does NOT warn in production", () => {
|
|
117
|
+
process.env.NODE_ENV = "production";
|
|
118
|
+
useSection();
|
|
119
|
+
usePartialSection();
|
|
120
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it("returns the placeholder URL even in production (still safe to embed)", () => {
|
|
124
|
+
process.env.NODE_ENV = "production";
|
|
125
|
+
expect(useSection()).toBe(HTMX_LEGACY_URL);
|
|
126
|
+
expect(usePartialSection()).toBe(HTMX_LEGACY_URL);
|
|
127
|
+
});
|
|
128
|
+
});
|
package/src/sdk/useScript.ts
CHANGED
|
@@ -150,21 +150,61 @@ export function inlineScript(js: string) {
|
|
|
150
150
|
* See: deco-to-tanstack-migration skill, "useComponent / partial sections"
|
|
151
151
|
* section, for the per-pattern recipes.
|
|
152
152
|
*
|
|
153
|
-
*
|
|
154
|
-
*
|
|
155
|
-
*
|
|
153
|
+
* ## SSR-safe stub behavior (since 2.27)
|
|
154
|
+
*
|
|
155
|
+
* Earlier versions threw on call. That broke SSR for the *entire page* if a
|
|
156
|
+
* single section still imported `useSection` — even sections that the user
|
|
157
|
+
* never interacts with (e.g. legacy login form on the homepage). React's
|
|
158
|
+
* error boundary would catch the throw and degrade the whole route to
|
|
159
|
+
* client rendering.
|
|
160
|
+
*
|
|
161
|
+
* The current behavior:
|
|
162
|
+
* - returns a stable placeholder URL (`HTMX_LEGACY_URL`) so it can be
|
|
163
|
+
* embedded in `hx-get` / `hx-post` attributes without crashing the
|
|
164
|
+
* SSR pass,
|
|
165
|
+
* - logs a deduped `console.warn` (in dev only) per call site so the
|
|
166
|
+
* migration signal stays loud,
|
|
167
|
+
* - the `htmx-residue` audit rule still catalogues every call site for
|
|
168
|
+
* systematic rewrite.
|
|
169
|
+
*
|
|
170
|
+
* This is a deliberate trade-off: SSR success > strict-throw enforcement.
|
|
171
|
+
* Audit + skill docs do the enforcement instead.
|
|
156
172
|
*/
|
|
157
173
|
const DEPRECATION_MESSAGE =
|
|
158
174
|
"[@decocms/start] useSection / usePartialSection were removed. " +
|
|
159
175
|
"The Fresh/Deno HTMX partial-section pattern does not apply on " +
|
|
160
176
|
"TanStack Start / Cloudflare Workers. Replace call-sites with " +
|
|
161
177
|
"createServerFn + useMutation, or local React state. See the " +
|
|
162
|
-
"deco-to-tanstack-migration skill for per-pattern recipes."
|
|
178
|
+
"deco-to-tanstack-migration skill for per-pattern recipes. " +
|
|
179
|
+
"Run `deco-post-cleanup` and look for rule [9] htmx-residue to find " +
|
|
180
|
+
"every site call-site that still depends on this.";
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Stable placeholder URL returned by the legacy `useSection` /
|
|
184
|
+
* `usePartialSection` stubs. Hitting this URL surfaces a clear error.
|
|
185
|
+
* Exported so frameworks/tests can match against it.
|
|
186
|
+
*/
|
|
187
|
+
export const HTMX_LEGACY_URL = "/__deco_legacy_htmx_section__";
|
|
188
|
+
|
|
189
|
+
function warnLegacyHtmx(name: string) {
|
|
190
|
+
if (typeof process !== "undefined" && process.env?.NODE_ENV === "production") {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
if (typeof (globalThis as any).__DECO_LEGACY_HTMX_WARNED === "undefined") {
|
|
194
|
+
(globalThis as any).__DECO_LEGACY_HTMX_WARNED = new Set<string>();
|
|
195
|
+
}
|
|
196
|
+
const set = (globalThis as any).__DECO_LEGACY_HTMX_WARNED as Set<string>;
|
|
197
|
+
if (set.has(name)) return;
|
|
198
|
+
set.add(name);
|
|
199
|
+
console.warn(`[${name}] ${DEPRECATION_MESSAGE}`);
|
|
200
|
+
}
|
|
163
201
|
|
|
164
|
-
export function usePartialSection(_props?: Record<string, unknown>):
|
|
165
|
-
|
|
202
|
+
export function usePartialSection(_props?: Record<string, unknown>): string {
|
|
203
|
+
warnLegacyHtmx("usePartialSection");
|
|
204
|
+
return HTMX_LEGACY_URL;
|
|
166
205
|
}
|
|
167
206
|
|
|
168
|
-
export function useSection(_props?: Record<string, unknown>):
|
|
169
|
-
|
|
207
|
+
export function useSection(_props?: Record<string, unknown>): string {
|
|
208
|
+
warnLegacyHtmx("useSection");
|
|
209
|
+
return HTMX_LEGACY_URL;
|
|
170
210
|
}
|