@sparkletree/cli 0.6.1 → 0.7.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/README.md CHANGED
@@ -22,12 +22,18 @@ sparkletree list
22
22
  sparkletree help
23
23
  ```
24
24
 
25
- `upgrade` ports hero/cta copies made by a 0.3.0 `add` to the 0.4.0 reservation
26
- wiring (the hold now reserves space with your fallback instead of rendering
27
- nothing; see LAYOUT-DOCTRINE.md in the repo). It rewrites a copy only where
28
- it still matches the 0.3.0 template exactly, ignoring the Tailwind classes you
29
- were invited to change; a copy edited where the wiring lands is left untouched
30
- and reported, never guessed at. `--dry-run` previews the verdicts.
25
+ `upgrade` ports hero/cta copies from any earlier `add` to the current template
26
+ wiring, one generation at a time: a 0.3.0 copy gets the 0.4.0 reservation
27
+ wiring (the hold reserves space with your fallback instead of rendering
28
+ nothing; see LAYOUT-DOCTRINE.md in the repo) and then the 0.6.1 change that
29
+ imports `safeActionUrl` from `@sparkletree/core` instead of freezing a local
30
+ copy; a 0.4.0/0.5.0 copy gets only the latter. What a copy is missing is
31
+ decided by its content, never by guessing a version. It rewrites a copy only
32
+ where it still matches the template it came from exactly, ignoring the
33
+ Tailwind classes you were invited to change; a copy edited where the wiring
34
+ lands is left untouched and reported, never guessed at. `--dry-run` previews
35
+ the verdicts, and nothing is written when the installed packages are older
36
+ than the rewritten copy needs to compile.
31
37
 
32
38
  | Option | What it does |
33
39
  |---|---|
package/dist/bin.js CHANGED
@@ -16,7 +16,7 @@ const HELP = `sparkletree — add SparkleTree components to your project
16
16
 
17
17
  Usage
18
18
  sparkletree add <component...> [options]
19
- sparkletree upgrade [options] port 0.3.0 copies to the 0.4.0 reservation wiring
19
+ sparkletree upgrade [options] port copied components to the current template wiring
20
20
  sparkletree list
21
21
  sparkletree help
22
22
 
@@ -0,0 +1,10 @@
1
+ /**
2
+ * The files `add` ACTUALLY wrote at v0.5.0, extracted from the tagged
3
+ * registry byte for byte (the same doctrine as the 0.3.0 fixtures in
4
+ * upgrade.test.ts: only the real output can falsify the codemod's `find`
5
+ * claims). Machine-generated from `git show v0.5.0:cli/src/registry.ts` —
6
+ * do not "tidy" these.
7
+ */
8
+ export declare const HERO_050 = "\"use client\";\n\nimport { StreamText, useIsland, withReserve, type UseIslandOptions } from \"@sparkletree/react\";\nimport type { FieldState } from \"@sparkletree/react\";\nimport { themeStyle } from \"@sparkletree/core\";\n\n/**\n * Your hero. Edit freely \u2014 this file is yours.\n *\n * What you should NOT need to touch, and what you will not find here: when the\n * copy paints, how long it holds, whether it types or lands composed, and\n * whether it carries the adaptation mark. All of that lives in\n * @sparkletree/core and @sparkletree/react so it can be fixed for you without\n * you re-copying this file.\n *\n * `fallback` is required. There is no loading state and no skeleton: if the\n * network is slow or the campaign is unreachable, this copy is what your\n * visitor reads. Write it as if it were the only version \u2014 often it is.\n *\n * Requires Tailwind CSS for the default classes \u2014 swap them freely.\n */\nexport interface HeroProps {\n campaignId: string;\n /** First paint. Real copy, never a placeholder. */\n fallback: UseIslandOptions[\"fallback\"];\n}\n\n/**\n * Server-driven URLs are configuration, not trust. Only http(s), mailto and\n * tel absolutes and relative navigations (\"/\", \"./\", \"#\", \"?\") pass; anything\n * else \u2014 javascript:, data:, an unprefixed string \u2014 is dropped.\n */\nfunction safeActionUrl(url: string | null | undefined): string | undefined {\n if (!url) return undefined;\n if (\n (url.startsWith(\"/\") && !url.startsWith(\"//\")) ||\n url.startsWith(\"./\") ||\n url.startsWith(\"#\") ||\n url.startsWith(\"?\")\n ) {\n return url;\n }\n try {\n const protocol = new URL(url).protocol;\n if ([\"https:\", \"http:\", \"mailto:\", \"tel:\"].includes(protocol)) return url;\n } catch {\n // Not a parseable URL at all.\n }\n return undefined;\n}\n\nexport function Hero({ campaignId, fallback }: HeroProps) {\n const { state, ref } = useIsland({ campaignId, island: \"hero\", fallback });\n const backgroundImage = safeActionUrl(state.backgroundImage);\n\n // The hold RESERVES (LAYOUT-DOCTRINE.md): while the canvas waits, each\n // field carries its fallback so StreamText keeps the geometry invisibly \u2014\n // nothing below the hero moves whichever way the wait ends. Delete this\n // and the section collapses during the hold; it is load-bearing.\n const holding = state.phase === \"holding\";\n const reserveOf = (name: \"greeting\" | \"headline\" | \"body\") =>\n state.held?.[name] || fallback[name];\n const held = (field: FieldState, reserve?: string) =>\n holding && reserve && !field.text ? withReserve(field, reserve) : field;\n\n return (\n <section\n ref={ref}\n className=\"relative overflow-hidden rounded-2xl p-10\"\n style={themeStyle(state.theme)}\n >\n {backgroundImage ? (\n <img\n src={backgroundImage}\n alt=\"\"\n className=\"absolute inset-0 h-full w-full object-cover\"\n />\n ) : null}\n\n <div className=\"relative\">\n {state.fields.greeting.text || (holding && reserveOf(\"greeting\")) ? (\n <StreamText\n field={held(state.fields.greeting, reserveOf(\"greeting\"))}\n as=\"p\"\n className=\"text-sm uppercase tracking-wide opacity-80\"\n />\n ) : null}\n\n {/* h2 to match the packaged Hero. The heading level is yours: pass\n as=\"h1\" if this section is your page's primary heading. */}\n <StreamText\n field={held(state.fields.headline, reserveOf(\"headline\"))}\n as=\"h2\"\n className=\"mt-2 text-4xl font-semibold\"\n />\n\n {state.fields.body.text || (holding && reserveOf(\"body\")) ? (\n <StreamText\n field={held(state.fields.body, reserveOf(\"body\"))}\n as=\"p\"\n className=\"mt-4 max-w-prose\"\n />\n ) : null}\n </div>\n </section>\n );\n}\n";
9
+ export declare const CTA_050 = "\"use client\";\n\nimport { StreamText, useIsland, useSparkletree, withReserve, type UseIslandOptions } from \"@sparkletree/react\";\nimport { readableTextColor, themeStyle, track } from \"@sparkletree/core\";\n\n/**\n * Your call to action. Edit freely.\n *\n * The button keeps its size while the label streams in, so the surrounding\n * layout does not shift under the visitor's cursor mid-click.\n *\n * Requires Tailwind CSS for the default classes \u2014 swap them freely.\n */\nexport interface CtaProps extends Omit<UseIslandOptions, \"island\"> {\n /** Where the button goes when the server has not resolved an action. */\n href?: string;\n onClick?: () => void;\n}\n\n/**\n * Server-driven URLs are configuration, not trust. Only http(s), mailto and\n * tel absolutes and relative navigations (\"/\", \"./\", \"#\", \"?\") pass; anything\n * else \u2014 javascript:, data:, an unprefixed string \u2014 is dropped.\n */\nfunction safeActionUrl(url: string | null | undefined): string | undefined {\n if (!url) return undefined;\n if (\n (url.startsWith(\"/\") && !url.startsWith(\"//\")) ||\n url.startsWith(\"./\") ||\n url.startsWith(\"#\") ||\n url.startsWith(\"?\")\n ) {\n return url;\n }\n try {\n const protocol = new URL(url).protocol;\n if ([\"https:\", \"http:\", \"mailto:\", \"tel:\"].includes(protocol)) return url;\n } catch {\n // Not a parseable URL at all.\n }\n return undefined;\n}\n\nexport function Cta(props: CtaProps) {\n const { href, onClick, ...islandOptions } = props;\n const { apiBase, organizationId } = useSparkletree();\n const { state, ref } = useIsland({ ...islandOptions, island: \"cta\" });\n // A rejected server action falls back to the href prop you control. The\n // prop is vetted too \u2014 the same render path, the same rules, one outcome.\n const action = safeActionUrl(state.ctaAction) ?? safeActionUrl(href);\n const { campaignId, surfaceId } = islandOptions;\n const variantId = state.variantId ?? undefined;\n // A surface-only Cta has no campaignId prop, but the click must still be\n // attributed: the server names the campaign it served in the terminal\n // done.meta, mirroring the impression path in useIsland.\n const metaCampaignId =\n typeof state.meta?.campaignId === \"string\" ? state.meta.campaignId : undefined;\n const resolvedCampaignId = campaignId ?? metaCampaignId;\n\n // The click is the other half of CTR. The SDK tells the server to suppress\n // its own counting for this page view \u2014 so if nothing here reports the\n // click, CTR for SDK traffic is a division by silence. Fire-and-forget:\n // track never throws and never blocks the navigation.\n const handleClick = () => {\n if (resolvedCampaignId) {\n void track(\n { apiBase, organizationId, campaignId: resolvedCampaignId, variantId, surfaceId },\n \"click\",\n { island: \"cta\" },\n );\n }\n onClick?.();\n };\n\n // The hold RESERVES (LAYOUT-DOCTRINE.md): a holding label rides the\n // fallback into StreamText invisibly, so the button keeps the size its\n // label will need. Delete this and the button collapses during the hold.\n const holding = state.phase === \"holding\";\n const ctaReserve = state.held?.cta || islandOptions.fallback.cta;\n const reserved = holding && !!ctaReserve && !state.fields.cta.text;\n const labelField = reserved\n ? withReserve(state.fields.cta, ctaReserve as string)\n : state.fields.cta;\n\n // Shared by the link and the no-destination fallback below.\n const shared = {\n onClick: handleClick,\n className:\n \"inline-flex min-h-11 min-w-40 items-center justify-center rounded-full px-6 font-medium\",\n style: {\n ...themeStyle(state.theme),\n // --st-cta-text is the label ink that reads on the theme's primary, so\n // a light theme can never produce a ghost button.\n ...(state.theme?.colors?.primary\n ? { [\"--st-cta-text\" as string]: readableTextColor(state.theme.colors.primary) }\n : {}),\n },\n // A CTA whose label is still empty is not yet a button a viewer can\n // meaningfully act on \u2014 but it must still occupy its space, or the\n // layout shifts when the copy lands.\n // A reserved button is full-size but its visible label is a hidden\n // reservation outside the a11y tree \u2014 never a NAMELESS control: the\n // reservation text is the honest accessible name.\n \"aria-disabled\": state.fields.cta.text ? undefined : true,\n \"aria-label\": reserved ? (ctaReserve as string) : undefined,\n };\n\n // Neither the stream nor you resolved a destination: render a\n // non-interactive element rather than an inert <a> with no href, which\n // some assistive tech still announces as a link to nowhere. Clicks are\n // still observed \u2014 engagement with the creative counts toward CTR whether\n // or not it navigates.\n if (action === undefined) {\n return (\n <span ref={ref as unknown as (element: HTMLSpanElement | null) => void} {...shared}>\n <StreamText field={labelField} />\n </span>\n );\n }\n\n return (\n <a ref={ref} href={action} {...shared}>\n <StreamText field={labelField} />\n </a>\n );\n}\n";
10
+ //# sourceMappingURL=fixtures-0.5.0.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures-0.5.0.d.ts","sourceRoot":"","sources":["../src/fixtures-0.5.0.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,eAAO,MAAM,QAAQ,q6HA2GpB,CAAC;AAEF,eAAO,MAAM,OAAO,yoKA8HnB,CAAC"}
@@ -0,0 +1,243 @@
1
+ /**
2
+ * The files `add` ACTUALLY wrote at v0.5.0, extracted from the tagged
3
+ * registry byte for byte (the same doctrine as the 0.3.0 fixtures in
4
+ * upgrade.test.ts: only the real output can falsify the codemod's `find`
5
+ * claims). Machine-generated from `git show v0.5.0:cli/src/registry.ts` —
6
+ * do not "tidy" these.
7
+ */
8
+ export const HERO_050 = `"use client";
9
+
10
+ import { StreamText, useIsland, withReserve, type UseIslandOptions } from "@sparkletree/react";
11
+ import type { FieldState } from "@sparkletree/react";
12
+ import { themeStyle } from "@sparkletree/core";
13
+
14
+ /**
15
+ * Your hero. Edit freely — this file is yours.
16
+ *
17
+ * What you should NOT need to touch, and what you will not find here: when the
18
+ * copy paints, how long it holds, whether it types or lands composed, and
19
+ * whether it carries the adaptation mark. All of that lives in
20
+ * @sparkletree/core and @sparkletree/react so it can be fixed for you without
21
+ * you re-copying this file.
22
+ *
23
+ * \`fallback\` is required. There is no loading state and no skeleton: if the
24
+ * network is slow or the campaign is unreachable, this copy is what your
25
+ * visitor reads. Write it as if it were the only version — often it is.
26
+ *
27
+ * Requires Tailwind CSS for the default classes — swap them freely.
28
+ */
29
+ export interface HeroProps {
30
+ campaignId: string;
31
+ /** First paint. Real copy, never a placeholder. */
32
+ fallback: UseIslandOptions["fallback"];
33
+ }
34
+
35
+ /**
36
+ * Server-driven URLs are configuration, not trust. Only http(s), mailto and
37
+ * tel absolutes and relative navigations ("/", "./", "#", "?") pass; anything
38
+ * else — javascript:, data:, an unprefixed string — is dropped.
39
+ */
40
+ function safeActionUrl(url: string | null | undefined): string | undefined {
41
+ if (!url) return undefined;
42
+ if (
43
+ (url.startsWith("/") && !url.startsWith("//")) ||
44
+ url.startsWith("./") ||
45
+ url.startsWith("#") ||
46
+ url.startsWith("?")
47
+ ) {
48
+ return url;
49
+ }
50
+ try {
51
+ const protocol = new URL(url).protocol;
52
+ if (["https:", "http:", "mailto:", "tel:"].includes(protocol)) return url;
53
+ } catch {
54
+ // Not a parseable URL at all.
55
+ }
56
+ return undefined;
57
+ }
58
+
59
+ export function Hero({ campaignId, fallback }: HeroProps) {
60
+ const { state, ref } = useIsland({ campaignId, island: "hero", fallback });
61
+ const backgroundImage = safeActionUrl(state.backgroundImage);
62
+
63
+ // The hold RESERVES (LAYOUT-DOCTRINE.md): while the canvas waits, each
64
+ // field carries its fallback so StreamText keeps the geometry invisibly —
65
+ // nothing below the hero moves whichever way the wait ends. Delete this
66
+ // and the section collapses during the hold; it is load-bearing.
67
+ const holding = state.phase === "holding";
68
+ const reserveOf = (name: "greeting" | "headline" | "body") =>
69
+ state.held?.[name] || fallback[name];
70
+ const held = (field: FieldState, reserve?: string) =>
71
+ holding && reserve && !field.text ? withReserve(field, reserve) : field;
72
+
73
+ return (
74
+ <section
75
+ ref={ref}
76
+ className="relative overflow-hidden rounded-2xl p-10"
77
+ style={themeStyle(state.theme)}
78
+ >
79
+ {backgroundImage ? (
80
+ <img
81
+ src={backgroundImage}
82
+ alt=""
83
+ className="absolute inset-0 h-full w-full object-cover"
84
+ />
85
+ ) : null}
86
+
87
+ <div className="relative">
88
+ {state.fields.greeting.text || (holding && reserveOf("greeting")) ? (
89
+ <StreamText
90
+ field={held(state.fields.greeting, reserveOf("greeting"))}
91
+ as="p"
92
+ className="text-sm uppercase tracking-wide opacity-80"
93
+ />
94
+ ) : null}
95
+
96
+ {/* h2 to match the packaged Hero. The heading level is yours: pass
97
+ as="h1" if this section is your page's primary heading. */}
98
+ <StreamText
99
+ field={held(state.fields.headline, reserveOf("headline"))}
100
+ as="h2"
101
+ className="mt-2 text-4xl font-semibold"
102
+ />
103
+
104
+ {state.fields.body.text || (holding && reserveOf("body")) ? (
105
+ <StreamText
106
+ field={held(state.fields.body, reserveOf("body"))}
107
+ as="p"
108
+ className="mt-4 max-w-prose"
109
+ />
110
+ ) : null}
111
+ </div>
112
+ </section>
113
+ );
114
+ }
115
+ `;
116
+ export const CTA_050 = `"use client";
117
+
118
+ import { StreamText, useIsland, useSparkletree, withReserve, type UseIslandOptions } from "@sparkletree/react";
119
+ import { readableTextColor, themeStyle, track } from "@sparkletree/core";
120
+
121
+ /**
122
+ * Your call to action. Edit freely.
123
+ *
124
+ * The button keeps its size while the label streams in, so the surrounding
125
+ * layout does not shift under the visitor's cursor mid-click.
126
+ *
127
+ * Requires Tailwind CSS for the default classes — swap them freely.
128
+ */
129
+ export interface CtaProps extends Omit<UseIslandOptions, "island"> {
130
+ /** Where the button goes when the server has not resolved an action. */
131
+ href?: string;
132
+ onClick?: () => void;
133
+ }
134
+
135
+ /**
136
+ * Server-driven URLs are configuration, not trust. Only http(s), mailto and
137
+ * tel absolutes and relative navigations ("/", "./", "#", "?") pass; anything
138
+ * else — javascript:, data:, an unprefixed string — is dropped.
139
+ */
140
+ function safeActionUrl(url: string | null | undefined): string | undefined {
141
+ if (!url) return undefined;
142
+ if (
143
+ (url.startsWith("/") && !url.startsWith("//")) ||
144
+ url.startsWith("./") ||
145
+ url.startsWith("#") ||
146
+ url.startsWith("?")
147
+ ) {
148
+ return url;
149
+ }
150
+ try {
151
+ const protocol = new URL(url).protocol;
152
+ if (["https:", "http:", "mailto:", "tel:"].includes(protocol)) return url;
153
+ } catch {
154
+ // Not a parseable URL at all.
155
+ }
156
+ return undefined;
157
+ }
158
+
159
+ export function Cta(props: CtaProps) {
160
+ const { href, onClick, ...islandOptions } = props;
161
+ const { apiBase, organizationId } = useSparkletree();
162
+ const { state, ref } = useIsland({ ...islandOptions, island: "cta" });
163
+ // A rejected server action falls back to the href prop you control. The
164
+ // prop is vetted too — the same render path, the same rules, one outcome.
165
+ const action = safeActionUrl(state.ctaAction) ?? safeActionUrl(href);
166
+ const { campaignId, surfaceId } = islandOptions;
167
+ const variantId = state.variantId ?? undefined;
168
+ // A surface-only Cta has no campaignId prop, but the click must still be
169
+ // attributed: the server names the campaign it served in the terminal
170
+ // done.meta, mirroring the impression path in useIsland.
171
+ const metaCampaignId =
172
+ typeof state.meta?.campaignId === "string" ? state.meta.campaignId : undefined;
173
+ const resolvedCampaignId = campaignId ?? metaCampaignId;
174
+
175
+ // The click is the other half of CTR. The SDK tells the server to suppress
176
+ // its own counting for this page view — so if nothing here reports the
177
+ // click, CTR for SDK traffic is a division by silence. Fire-and-forget:
178
+ // track never throws and never blocks the navigation.
179
+ const handleClick = () => {
180
+ if (resolvedCampaignId) {
181
+ void track(
182
+ { apiBase, organizationId, campaignId: resolvedCampaignId, variantId, surfaceId },
183
+ "click",
184
+ { island: "cta" },
185
+ );
186
+ }
187
+ onClick?.();
188
+ };
189
+
190
+ // The hold RESERVES (LAYOUT-DOCTRINE.md): a holding label rides the
191
+ // fallback into StreamText invisibly, so the button keeps the size its
192
+ // label will need. Delete this and the button collapses during the hold.
193
+ const holding = state.phase === "holding";
194
+ const ctaReserve = state.held?.cta || islandOptions.fallback.cta;
195
+ const reserved = holding && !!ctaReserve && !state.fields.cta.text;
196
+ const labelField = reserved
197
+ ? withReserve(state.fields.cta, ctaReserve as string)
198
+ : state.fields.cta;
199
+
200
+ // Shared by the link and the no-destination fallback below.
201
+ const shared = {
202
+ onClick: handleClick,
203
+ className:
204
+ "inline-flex min-h-11 min-w-40 items-center justify-center rounded-full px-6 font-medium",
205
+ style: {
206
+ ...themeStyle(state.theme),
207
+ // --st-cta-text is the label ink that reads on the theme's primary, so
208
+ // a light theme can never produce a ghost button.
209
+ ...(state.theme?.colors?.primary
210
+ ? { ["--st-cta-text" as string]: readableTextColor(state.theme.colors.primary) }
211
+ : {}),
212
+ },
213
+ // A CTA whose label is still empty is not yet a button a viewer can
214
+ // meaningfully act on — but it must still occupy its space, or the
215
+ // layout shifts when the copy lands.
216
+ // A reserved button is full-size but its visible label is a hidden
217
+ // reservation outside the a11y tree — never a NAMELESS control: the
218
+ // reservation text is the honest accessible name.
219
+ "aria-disabled": state.fields.cta.text ? undefined : true,
220
+ "aria-label": reserved ? (ctaReserve as string) : undefined,
221
+ };
222
+
223
+ // Neither the stream nor you resolved a destination: render a
224
+ // non-interactive element rather than an inert <a> with no href, which
225
+ // some assistive tech still announces as a link to nowhere. Clicks are
226
+ // still observed — engagement with the creative counts toward CTR whether
227
+ // or not it navigates.
228
+ if (action === undefined) {
229
+ return (
230
+ <span ref={ref as unknown as (element: HTMLSpanElement | null) => void} {...shared}>
231
+ <StreamText field={labelField} />
232
+ </span>
233
+ );
234
+ }
235
+
236
+ return (
237
+ <a ref={ref} href={action} {...shared}>
238
+ <StreamText field={labelField} />
239
+ </a>
240
+ );
241
+ }
242
+ `;
243
+ //# sourceMappingURL=fixtures-0.5.0.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fixtures-0.5.0.js","sourceRoot":"","sources":["../src/fixtures-0.5.0.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,CAAC,MAAM,QAAQ,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA2GvB,CAAC;AAEF,MAAM,CAAC,MAAM,OAAO,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA8HtB,CAAC"}
package/dist/upgrade.d.ts CHANGED
@@ -1,42 +1,60 @@
1
1
  /**
2
2
  * `sparkletree upgrade`
3
3
  *
4
- * The 0.3.0 → 0.4.0 codemod for copied components. Copies are the customer's
5
- * files (see registry.ts), so the SDK cannot patch them at runtime — but 0.4.0
6
- * moved the hold from blanking to RESERVING (LAYOUT-DOCTRINE.md), and a copy
7
- * without the reservation wiring still collapses during the hold. This command
8
- * ports that wiring into copies that came from `add` at 0.3.0.
4
+ * Version-aware codemods for copied components. Copies are the customer's
5
+ * files (see registry.ts), so the SDK cannot patch them at runtime; this
6
+ * command ports each GENERATION of template wiring into copies that predate
7
+ * it. A copy from `add` at 0.3.0 gets every generation in order; a 0.5.0
8
+ * copy gets only the generations it is missing. Which generations a copy is
9
+ * missing is decided by content (each generation's marker), never by
10
+ * guessing a version.
9
11
  *
10
12
  * How it stays safe in someone else's repository:
11
13
  *
12
- * - exact-substring hunks, all-or-nothing per file. Every hunk must match
13
- * exactly once (or, where marked, at least once) before anything is
14
- * written. One miss means the file has diverged from the template at a
15
- * line the upgrade needs, and guessing at edited code is how a tool
16
- * "reverts three months of my hero" — so we write nothing and say which
17
- * hunks failed, so the wiring can be ported by hand;
14
+ * - exact-substring hunks, all-or-nothing per file. Every hunk of every
15
+ * needed generation must match exactly once (or, where marked, at least
16
+ * once) before anything is written. One miss means the file has diverged
17
+ * from the template at a line the upgrade needs, and guessing at edited
18
+ * code is how a tool "reverts three months of my hero" — so we write
19
+ * nothing and say which hunks failed, so the wiring can be ported by
20
+ * hand;
18
21
  * - hunks avoid the Tailwind classNames, which `add` explicitly invites
19
22
  * customers to change. A restyled copy still upgrades;
20
- * - idempotent: a file that already has the wiring (it imports
21
- * `withReserve`) is reported up to date and never touched;
23
+ * - idempotent: a generation whose marker is already in the file is
24
+ * skipped, and a copy missing none is reported up to date and never
25
+ * touched;
22
26
  * - `--dry-run` reports the same verdicts and writes nothing.
23
27
  */
24
28
  interface Hunk {
25
29
  /** For the skipped-report: which part of the wiring this is. */
26
30
  label: string;
27
- /** Exact text as `add` wrote it at 0.3.0. */
31
+ /** Exact text as `add` wrote it, byte for byte. */
28
32
  find: string;
29
33
  replace: string;
30
34
  /** Replace every occurrence instead of requiring exactly one. */
31
35
  all?: boolean;
32
36
  }
37
+ interface Codemod {
38
+ /**
39
+ * Already-upgraded detection: presence of this marker means this
40
+ * generation's wiring is in the copy, so the generation is skipped.
41
+ */
42
+ marker: string;
43
+ /**
44
+ * Minimum installed @sparkletree/react (core, react and cli version
45
+ * together) the REWRITTEN copy compiles against. Below it the run behaves
46
+ * as a dry run — writing code an old install cannot compile trades a
47
+ * cosmetic gap for a build error.
48
+ */
49
+ needs: string;
50
+ hunks: Hunk[];
51
+ }
33
52
  export interface UpgradeTarget {
34
53
  component: string;
35
54
  /** Where `add` writes it, relative to the target directory. */
36
55
  path: string;
37
- /** Already-upgraded detection: 0.3.0 copies never mention withReserve. */
38
- marker: string;
39
- hunks: Hunk[];
56
+ /** Oldest first; a copy gets every generation it is missing, in order. */
57
+ codemods: readonly Codemod[];
40
58
  }
41
59
  export declare const UPGRADE_TARGETS: readonly UpgradeTarget[];
42
60
  export interface UpgradeOptions {
@@ -56,13 +74,19 @@ export interface UpgradeResult {
56
74
  hunks: string[];
57
75
  }[];
58
76
  /**
59
- * The installed @sparkletree/react version, when it predates the wiring.
60
- * The upgraded source reads `state.held`, which does not exist before
61
- * 0.4.0 writing it against an old install trades a layout gap for a
62
- * compile error. When set, the run behaved as a dry run: `upgraded` lists
63
- * what WOULD be written once the packages are updated.
77
+ * The installed @sparkletree/react version, when it predates
78
+ * `requiredReact`. Writing code an old install cannot compile trades a
79
+ * cosmetic gap for a build error, so when set the run behaved as a dry
80
+ * run: `upgraded` lists what WOULD be written once the packages are
81
+ * updated.
64
82
  */
65
83
  blockedByReact: string | null;
84
+ /**
85
+ * The highest `needs` among the generations this run wanted to apply —
86
+ * what to update @sparkletree/react (and core) to when blocked. Null when
87
+ * nothing needed upgrading.
88
+ */
89
+ requiredReact: string | null;
66
90
  }
67
91
  export declare function upgrade(options?: UpgradeOptions): Promise<UpgradeResult>;
68
92
  /** Human-readable summary. Mirrors what `upgrade` actually did. */
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAKH,UAAU,IAAI;IACZ,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAsBD,eAAO,MAAM,eAAe,EAAE,SAAS,aAAa,EAmKnD,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yEAAyE;IACzE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IAC9C;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;CAC/B;AAmCD,wBAAsB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAoDlF;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,UAAQ,GAAG,MAAM,CA4CjF"}
1
+ {"version":3,"file":"upgrade.d.ts","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAKH,UAAU,IAAI;IACZ,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,mDAAmD;IACnD,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,iEAAiE;IACjE,GAAG,CAAC,EAAE,OAAO,CAAC;CACf;AAED,UAAU,OAAO;IACf;;;OAGG;IACH,MAAM,EAAE,MAAM,CAAC;IACf;;;;;OAKG;IACH,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,IAAI,EAAE,CAAC;CACf;AAED,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,+DAA+D;IAC/D,IAAI,EAAE,MAAM,CAAC;IACb,0EAA0E;IAC1E,QAAQ,EAAE,SAAS,OAAO,EAAE,CAAC;CAC9B;AAsDD,eAAO,MAAM,eAAe,EAAE,SAAS,aAAa,EA8InD,CAAC;AAEF,MAAM,WAAW,cAAc;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,0EAA0E;IAC1E,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,yEAAyE;IACzE,QAAQ,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,EAAE,CAAA;KAAE,EAAE,CAAC;IAC9C;;;;;;OAMG;IACH,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B;;;;OAIG;IACH,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAgDD,wBAAsB,OAAO,CAAC,OAAO,GAAE,cAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CA8ElF;AAED,mEAAmE;AACnE,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,aAAa,EAAE,MAAM,UAAQ,GAAG,MAAM,CA6CjF"}
package/dist/upgrade.js CHANGED
@@ -1,24 +1,28 @@
1
1
  /**
2
2
  * `sparkletree upgrade`
3
3
  *
4
- * The 0.3.0 → 0.4.0 codemod for copied components. Copies are the customer's
5
- * files (see registry.ts), so the SDK cannot patch them at runtime — but 0.4.0
6
- * moved the hold from blanking to RESERVING (LAYOUT-DOCTRINE.md), and a copy
7
- * without the reservation wiring still collapses during the hold. This command
8
- * ports that wiring into copies that came from `add` at 0.3.0.
4
+ * Version-aware codemods for copied components. Copies are the customer's
5
+ * files (see registry.ts), so the SDK cannot patch them at runtime; this
6
+ * command ports each GENERATION of template wiring into copies that predate
7
+ * it. A copy from `add` at 0.3.0 gets every generation in order; a 0.5.0
8
+ * copy gets only the generations it is missing. Which generations a copy is
9
+ * missing is decided by content (each generation's marker), never by
10
+ * guessing a version.
9
11
  *
10
12
  * How it stays safe in someone else's repository:
11
13
  *
12
- * - exact-substring hunks, all-or-nothing per file. Every hunk must match
13
- * exactly once (or, where marked, at least once) before anything is
14
- * written. One miss means the file has diverged from the template at a
15
- * line the upgrade needs, and guessing at edited code is how a tool
16
- * "reverts three months of my hero" — so we write nothing and say which
17
- * hunks failed, so the wiring can be ported by hand;
14
+ * - exact-substring hunks, all-or-nothing per file. Every hunk of every
15
+ * needed generation must match exactly once (or, where marked, at least
16
+ * once) before anything is written. One miss means the file has diverged
17
+ * from the template at a line the upgrade needs, and guessing at edited
18
+ * code is how a tool "reverts three months of my hero" — so we write
19
+ * nothing and say which hunks failed, so the wiring can be ported by
20
+ * hand;
18
21
  * - hunks avoid the Tailwind classNames, which `add` explicitly invites
19
22
  * customers to change. A restyled copy still upgrades;
20
- * - idempotent: a file that already has the wiring (it imports
21
- * `withReserve`) is reported up to date and never touched;
23
+ * - idempotent: a generation whose marker is already in the file is
24
+ * skipped, and a copy missing none is reported up to date and never
25
+ * touched;
22
26
  * - `--dry-run` reports the same verdicts and writes nothing.
23
27
  */
24
28
  import { readFile, writeFile } from "node:fs/promises";
@@ -41,39 +45,13 @@ const CTA_RESERVATION = ` // The hold RESERVES (LAYOUT-DOCTRINE.md): a holding
41
45
  const labelField = reserved
42
46
  ? withReserve(state.fields.cta, ctaReserve as string)
43
47
  : state.fields.cta;`;
44
- export const UPGRADE_TARGETS = [
45
- {
46
- component: "hero",
47
- path: "sparkletree/hero.tsx",
48
- marker: "withReserve",
49
- hunks: [
50
- {
51
- label: "disclosure note",
52
- find: ` * copy paints, how long it holds, whether it types or lands composed, and
53
- * whether it carries the adaptation mark. All of that lives in
54
- * @sparkletree/core and @sparkletree/react so it can be fixed for you without
55
- * you re-copying this file.`,
56
- replace: ` * copy paints, how long it holds, whether it types or lands composed, and the
57
- * provenance every adapted field carries. All of that lives in
58
- * @sparkletree/core and @sparkletree/react so it can be fixed for you without
59
- * you re-copying this file. No visible AI-disclosure mark renders by default —
60
- * if you want one, render \`InlineMark\` from @sparkletree/react where
61
- * \`decideChrome(field.source, prefersStandard).marked\` is true.`,
62
- },
63
- {
64
- label: "imports",
65
- find: `import { StreamText, useIsland, type UseIslandOptions } from "@sparkletree/react";`,
66
- replace: `import { StreamText, useIsland, withReserve, type UseIslandOptions } from "@sparkletree/react";
67
- import type { FieldState } from "@sparkletree/react";`,
68
- },
69
- {
70
- label: "core imports",
71
- find: `import { themeStyle } from "@sparkletree/core";`,
72
- replace: `import { safeActionUrl, themeStyle } from "@sparkletree/core";`,
73
- },
74
- {
75
- label: "shared safeActionUrl",
76
- find: `/**
48
+ /**
49
+ * The pre-0.6.1 inline vetting helper, byte for byte as `add` wrote it into
50
+ * every hero and cta copy from 0.3.0 through 0.5.0. 0.6.1 moved the one
51
+ * implementation to @sparkletree/core — deleting this block and importing
52
+ * the shared helper IS that generation.
53
+ */
54
+ const INLINE_SAFE_ACTION_URL = `/**
77
55
  * Server-driven URLs are configuration, not trust. Only http(s), mailto and
78
56
  * tel absolutes and relative navigations ("/", "./", "#", "?") pass; anything
79
57
  * else — javascript:, data:, an unprefixed string — is dropped.
@@ -97,110 +75,146 @@ function safeActionUrl(url: string | null | undefined): string | undefined {
97
75
  return undefined;
98
76
  }
99
77
 
100
- `,
101
- replace: ``,
102
- },
78
+ `;
79
+ export const UPGRADE_TARGETS = [
80
+ {
81
+ component: "hero",
82
+ path: "sparkletree/hero.tsx",
83
+ codemods: [
103
84
  {
104
- label: "reservation helpers",
105
- find: `const backgroundImage = safeActionUrl(state.backgroundImage);`,
106
- replace: `const backgroundImage = safeActionUrl(state.backgroundImage);
85
+ // 0.4.0: the hold RESERVES instead of blanking (LAYOUT-DOCTRINE.md).
86
+ marker: "withReserve",
87
+ needs: "0.4.0",
88
+ hunks: [
89
+ {
90
+ label: "imports",
91
+ find: `import { StreamText, useIsland, type UseIslandOptions } from "@sparkletree/react";`,
92
+ replace: `import { StreamText, useIsland, withReserve, type UseIslandOptions } from "@sparkletree/react";
93
+ import type { FieldState } from "@sparkletree/react";`,
94
+ },
95
+ {
96
+ label: "reservation helpers",
97
+ find: `const backgroundImage = safeActionUrl(state.backgroundImage);`,
98
+ replace: `const backgroundImage = safeActionUrl(state.backgroundImage);
107
99
 
108
100
  ${HERO_RESERVATION}`,
101
+ },
102
+ {
103
+ label: "greeting condition",
104
+ find: `{state.fields.greeting.text ? (`,
105
+ replace: `{state.fields.greeting.text || (holding && reserveOf("greeting")) ? (`,
106
+ },
107
+ {
108
+ label: "greeting field",
109
+ find: `field={state.fields.greeting}`,
110
+ replace: `field={held(state.fields.greeting, reserveOf("greeting"))}`,
111
+ },
112
+ {
113
+ label: "headline field",
114
+ find: `field={state.fields.headline}`,
115
+ replace: `field={held(state.fields.headline, reserveOf("headline"))}`,
116
+ },
117
+ {
118
+ label: "body condition",
119
+ find: `{state.fields.body.text ? (`,
120
+ replace: `{state.fields.body.text || (holding && reserveOf("body")) ? (`,
121
+ },
122
+ {
123
+ label: "body field",
124
+ find: `field={state.fields.body}`,
125
+ replace: `field={held(state.fields.body, reserveOf("body"))}`,
126
+ },
127
+ ],
109
128
  },
110
129
  {
111
- label: "greeting condition",
112
- find: `{state.fields.greeting.text ? (`,
113
- replace: `{state.fields.greeting.text || (holding && reserveOf("greeting")) ? (`,
114
- },
115
- {
116
- label: "greeting field",
117
- find: `field={state.fields.greeting}`,
118
- replace: `field={held(state.fields.greeting, reserveOf("greeting"))}`,
119
- },
120
- {
121
- label: "headline field",
122
- find: `field={state.fields.headline}`,
123
- replace: `field={held(state.fields.headline, reserveOf("headline"))}`,
124
- },
125
- {
126
- label: "body condition",
127
- find: `{state.fields.body.text ? (`,
128
- replace: `{state.fields.body.text || (holding && reserveOf("body")) ? (`,
129
- },
130
- {
131
- label: "body field",
132
- find: `field={state.fields.body}`,
133
- replace: `field={held(state.fields.body, reserveOf("body"))}`,
130
+ // 0.6.1: one safeActionUrl, imported from core; the header stops
131
+ // claiming a default disclosure mark is handled upstream.
132
+ marker: `safeActionUrl, themeStyle } from "@sparkletree/core"`,
133
+ needs: "0.6.1",
134
+ hunks: [
135
+ {
136
+ label: "disclosure note",
137
+ find: ` * copy paints, how long it holds, whether it types or lands composed, and
138
+ * whether it carries the adaptation mark. All of that lives in
139
+ * @sparkletree/core and @sparkletree/react so it can be fixed for you without
140
+ * you re-copying this file.`,
141
+ replace: ` * copy paints, how long it holds, whether it types or lands composed, and the
142
+ * provenance every adapted field carries. All of that lives in
143
+ * @sparkletree/core and @sparkletree/react so it can be fixed for you without
144
+ * you re-copying this file. No visible AI-disclosure mark renders by default —
145
+ * if you want one, render \`InlineMark\` from @sparkletree/react where
146
+ * \`decideChrome(field.source, prefersStandard).marked\` is true.`,
147
+ },
148
+ {
149
+ label: "core imports",
150
+ find: `import { themeStyle } from "@sparkletree/core";`,
151
+ replace: `import { safeActionUrl, themeStyle } from "@sparkletree/core";`,
152
+ },
153
+ {
154
+ label: "shared safeActionUrl",
155
+ find: INLINE_SAFE_ACTION_URL,
156
+ replace: ``,
157
+ },
158
+ ],
134
159
  },
135
160
  ],
136
161
  },
137
162
  {
138
163
  component: "cta",
139
164
  path: "sparkletree/cta.tsx",
140
- marker: "withReserve",
141
- hunks: [
142
- {
143
- label: "imports",
144
- find: `import { StreamText, useIsland, useSparkletree, type UseIslandOptions } from "@sparkletree/react";`,
145
- replace: `import { StreamText, useIsland, useSparkletree, withReserve, type UseIslandOptions } from "@sparkletree/react";`,
146
- },
147
- {
148
- label: "core imports",
149
- find: `import { readableTextColor, themeStyle, track } from "@sparkletree/core";`,
150
- replace: `import { readableTextColor, safeActionUrl, themeStyle, track } from "@sparkletree/core";`,
151
- },
152
- {
153
- label: "shared safeActionUrl",
154
- find: `/**
155
- * Server-driven URLs are configuration, not trust. Only http(s), mailto and
156
- * tel absolutes and relative navigations ("/", "./", "#", "?") pass; anything
157
- * else — javascript:, data:, an unprefixed string — is dropped.
158
- */
159
- function safeActionUrl(url: string | null | undefined): string | undefined {
160
- if (!url) return undefined;
161
- if (
162
- (url.startsWith("/") && !url.startsWith("//")) ||
163
- url.startsWith("./") ||
164
- url.startsWith("#") ||
165
- url.startsWith("?")
166
- ) {
167
- return url;
168
- }
169
- try {
170
- const protocol = new URL(url).protocol;
171
- if (["https:", "http:", "mailto:", "tel:"].includes(protocol)) return url;
172
- } catch {
173
- // Not a parseable URL at all.
174
- }
175
- return undefined;
176
- }
177
-
178
- `,
179
- replace: ``,
180
- },
165
+ codemods: [
181
166
  {
182
- label: "reservation helpers",
183
- find: ` onClick?.();
167
+ // 0.4.0: the hold RESERVES instead of blanking (LAYOUT-DOCTRINE.md).
168
+ marker: "withReserve",
169
+ needs: "0.4.0",
170
+ hunks: [
171
+ {
172
+ label: "imports",
173
+ find: `import { StreamText, useIsland, useSparkletree, type UseIslandOptions } from "@sparkletree/react";`,
174
+ replace: `import { StreamText, useIsland, useSparkletree, withReserve, type UseIslandOptions } from "@sparkletree/react";`,
175
+ },
176
+ {
177
+ label: "reservation helpers",
178
+ find: ` onClick?.();
184
179
  };`,
185
- replace: ` onClick?.();
180
+ replace: ` onClick?.();
186
181
  };
187
182
 
188
183
  ${CTA_RESERVATION}`,
189
- },
190
- {
191
- label: "accessible name",
192
- find: ` "aria-disabled": state.fields.cta.text ? undefined : true,`,
193
- replace: ` // A reserved button is full-size but its visible label is a hidden
184
+ },
185
+ {
186
+ label: "accessible name",
187
+ find: ` "aria-disabled": state.fields.cta.text ? undefined : true,`,
188
+ replace: ` // A reserved button is full-size but its visible label is a hidden
194
189
  // reservation outside the a11y tree — never a NAMELESS control: the
195
190
  // reservation text is the honest accessible name.
196
191
  "aria-disabled": state.fields.cta.text ? undefined : true,
197
192
  "aria-label": reserved ? (ctaReserve as string) : undefined,`,
193
+ },
194
+ {
195
+ label: "label field",
196
+ find: `<StreamText field={state.fields.cta} />`,
197
+ replace: `<StreamText field={labelField} />`,
198
+ all: true,
199
+ },
200
+ ],
198
201
  },
199
202
  {
200
- label: "label field",
201
- find: `<StreamText field={state.fields.cta} />`,
202
- replace: `<StreamText field={labelField} />`,
203
- all: true,
203
+ // 0.6.1: one safeActionUrl, imported from core.
204
+ marker: `readableTextColor, safeActionUrl, themeStyle, track } from "@sparkletree/core"`,
205
+ needs: "0.6.1",
206
+ hunks: [
207
+ {
208
+ label: "core imports",
209
+ find: `import { readableTextColor, themeStyle, track } from "@sparkletree/core";`,
210
+ replace: `import { readableTextColor, safeActionUrl, themeStyle, track } from "@sparkletree/core";`,
211
+ },
212
+ {
213
+ label: "shared safeActionUrl",
214
+ find: INLINE_SAFE_ACTION_URL,
215
+ replace: ``,
216
+ },
217
+ ],
204
218
  },
205
219
  ],
206
220
  },
@@ -228,25 +242,43 @@ async function installedReactVersion(cwd) {
228
242
  dir = parent;
229
243
  }
230
244
  }
231
- function supportsReservation(version) {
232
- const [major = 0, minor = 0] = version.split(".").map(Number);
233
- // Unparseable versions are not evidence of an old install.
234
- if (!Number.isFinite(major) || !Number.isFinite(minor))
235
- return true;
236
- return major > 0 || minor >= 4;
245
+ /** installed >= needs, on plain major.minor.patch. Unparseable installed
246
+ * versions are not evidence of an old install and pass. */
247
+ function versionAtLeast(installed, needs) {
248
+ const have = installed.split(".").map(Number);
249
+ const want = needs.split(".").map(Number);
250
+ for (let i = 0; i < 3; i++) {
251
+ const a = have[i] ?? 0;
252
+ const b = want[i] ?? 0;
253
+ if (!Number.isFinite(a))
254
+ return true;
255
+ if (a !== b)
256
+ return a > b;
257
+ }
258
+ return true;
259
+ }
260
+ /** The larger of two plain versions, for accumulating `requiredReact`. */
261
+ function maxVersion(a, b) {
262
+ if (a === null)
263
+ return b;
264
+ return versionAtLeast(a, b) ? a : b;
237
265
  }
238
266
  export async function upgrade(options = {}) {
239
267
  const cwd = options.cwd ?? process.cwd();
240
268
  const targetDir = options.targetDir ?? "components";
241
269
  const reactVersion = await installedReactVersion(cwd);
242
- const blockedByReact = reactVersion !== null && !supportsReservation(reactVersion) ? reactVersion : null;
243
270
  const result = {
244
271
  upgraded: [],
245
272
  upToDate: [],
246
273
  missing: [],
247
274
  diverged: [],
248
- blockedByReact,
275
+ blockedByReact: null,
276
+ requiredReact: null,
249
277
  };
278
+ // First pass: which generations does each copy still need? The block
279
+ // decision needs the answer across ALL files — updating packages is one
280
+ // action, so the run names one required version, not one per file.
281
+ const pending = [];
250
282
  for (const target of UPGRADE_TARGETS) {
251
283
  const absolute = resolve(cwd, targetDir, target.path);
252
284
  const path = relative(cwd, absolute);
@@ -258,25 +290,45 @@ export async function upgrade(options = {}) {
258
290
  result.missing.push(path);
259
291
  continue;
260
292
  }
261
- if (content.includes(target.marker)) {
293
+ const needed = target.codemods.filter((codemod) => !content.includes(codemod.marker));
294
+ if (!needed.length) {
262
295
  result.upToDate.push(path);
263
296
  continue;
264
297
  }
265
- const failed = target.hunks
266
- .filter((hunk) => {
267
- const count = occurrences(content, hunk.find);
268
- return hunk.all ? count === 0 : count !== 1;
269
- })
270
- .map((hunk) => hunk.label);
298
+ for (const codemod of needed) {
299
+ result.requiredReact = maxVersion(result.requiredReact, codemod.needs);
300
+ }
301
+ pending.push({ absolute, path, content, needed });
302
+ }
303
+ if (reactVersion !== null &&
304
+ result.requiredReact !== null &&
305
+ !versionAtLeast(reactVersion, result.requiredReact)) {
306
+ result.blockedByReact = reactVersion;
307
+ }
308
+ // Second pass: validate and apply the needed generations IN ORDER against
309
+ // the evolving text — a later generation's hunks land on the earlier
310
+ // generation's output, exactly as they did across real releases. Still
311
+ // all-or-nothing per file: one miss anywhere and nothing is written.
312
+ for (const { absolute, path, content, needed } of pending) {
313
+ let next = content;
314
+ const failed = [];
315
+ for (const codemod of needed) {
316
+ for (const hunk of codemod.hunks) {
317
+ const count = occurrences(next, hunk.find);
318
+ if (hunk.all ? count === 0 : count !== 1) {
319
+ failed.push(hunk.label);
320
+ continue;
321
+ }
322
+ next = hunk.all
323
+ ? next.replaceAll(hunk.find, hunk.replace)
324
+ : next.replace(hunk.find, hunk.replace);
325
+ }
326
+ }
271
327
  if (failed.length) {
272
328
  result.diverged.push({ path, hunks: failed });
273
329
  continue;
274
330
  }
275
- let next = content;
276
- for (const hunk of target.hunks) {
277
- next = hunk.all ? next.replaceAll(hunk.find, hunk.replace) : next.replace(hunk.find, hunk.replace);
278
- }
279
- if (!options.dryRun && !blockedByReact)
331
+ if (!options.dryRun && !result.blockedByReact)
280
332
  await writeFile(absolute, next, "utf8");
281
333
  result.upgraded.push(path);
282
334
  }
@@ -287,10 +339,10 @@ export function formatUpgradeResult(result, dryRun = false) {
287
339
  const lines = [];
288
340
  const verb = dryRun || result.blockedByReact ? "Would upgrade" : "Upgraded";
289
341
  if (result.blockedByReact) {
290
- lines.push(`Nothing written: @sparkletree/react ${result.blockedByReact} is installed,`, "and the reservation wiring needs ^0.4.0 (`state.held` does not exist", "before it). Update @sparkletree/react and @sparkletree/core, then", "re-run `sparkletree upgrade`.", "");
342
+ lines.push(`Nothing written: @sparkletree/react ${result.blockedByReact} is installed,`, `and this upgrade's wiring needs ^${result.requiredReact ?? "0.4.0"} to compile.`, "Update @sparkletree/react and @sparkletree/core, then re-run", "`sparkletree upgrade`.", "");
291
343
  }
292
344
  if (result.upgraded.length) {
293
- lines.push(`${verb} to the 0.4.0 reservation wiring:`);
345
+ lines.push(`${verb} to the current template wiring:`);
294
346
  for (const path of result.upgraded)
295
347
  lines.push(` ${path}`);
296
348
  }
@@ -304,12 +356,12 @@ export function formatUpgradeResult(result, dryRun = false) {
304
356
  for (const item of result.diverged) {
305
357
  lines.push(` ${item.path} — did not match at: ${item.hunks.join(", ")}`);
306
358
  }
307
- lines.push("", "Nothing was written to those files. Port the reservation wiring by", "hand (the current templates show it: `sparkletree add <name> --dir", "/tmp/fresh` writes a reference copy), or re-copy with `add --force`", "if you are happy to lose your edits.");
359
+ lines.push("", "Nothing was written to those files. Port the wiring by hand (the", "current templates show it: `sparkletree add <name> --dir /tmp/fresh`", "writes a reference copy), or re-copy with `add --force` if you are", "happy to lose your edits.");
308
360
  }
309
361
  if (!lines.length) {
310
362
  lines.push("Nothing to upgrade — no copied hero or cta found.");
311
363
  }
312
- lines.push("", "Why: 0.4.0 holds by RESERVING space with the fallback instead of", "rendering nothing. Without this wiring a copied hero or cta collapses", "during the hold and the page shifts when copy arrives.");
364
+ lines.push("", "Why: copies drift. 0.4.0 holds by RESERVING space with the fallback", "(without it a copied hero or cta collapses during the hold), and 0.6.1", "moved URL vetting to one implementation in @sparkletree/core so security", "fixes reach copies through npm instead of a migration guide.");
313
365
  return lines.join("\n");
314
366
  }
315
367
  //# sourceMappingURL=upgrade.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"upgrade.js","sourceRoot":"","sources":["../src/upgrade.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sparkletree/cli",
3
- "version": "0.6.1",
3
+ "version": "0.7.0",
4
4
  "description": "sparkletree — add SparkleTree components to your project.",
5
5
  "license": "MIT",
6
6
  "type": "module",