create-volt 0.34.0 → 0.35.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/CHANGELOG.md CHANGED
@@ -4,6 +4,15 @@ All notable changes to `create-volt` are documented here. The format follows
4
4
  [Keep a Changelog](https://keepachangelog.com/), and this project adheres to
5
5
  [Semantic Versioning](https://semver.org/).
6
6
 
7
+ ## [0.35.0] - 2026-06-29
8
+
9
+ ### Fixed
10
+ - **Setup wizard: dependency add-ons now show as checked.** Enabling `auth` pulls
11
+ in `db` + `mailer` (its dependencies), which were added to `VOLT_ADDONS` but
12
+ whose checkboxes stayed *unchecked* — so the generated `.env` looked like it had
13
+ add-ons you never picked. Pulled-in dependencies now render **checked + disabled**
14
+ with a "required by <add-on>" note, so the checkboxes always match `VOLT_ADDONS`.
15
+
7
16
  ## [0.34.0] - 2026-06-29
8
17
 
9
18
  ### Added
@@ -454,6 +463,7 @@ All notable changes to `create-volt` are documented here. The format follows
454
463
  watching and full-page hot reload. Supports `--skip-install` and `--force`,
455
464
  and auto-detects npm / pnpm / yarn / bun for the install step.
456
465
 
466
+ [0.35.0]: https://github.com/MIR-2025/volt/releases/tag/v0.35.0
457
467
  [0.34.0]: https://github.com/MIR-2025/volt/releases/tag/v0.34.0
458
468
  [0.33.0]: https://github.com/MIR-2025/volt/releases/tag/v0.33.0
459
469
  [0.32.0]: https://github.com/MIR-2025/volt/releases/tag/v0.32.0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-volt",
3
- "version": "0.34.0",
3
+ "version": "0.35.0",
4
4
  "description": "Scaffold a new Volt app — no-build, signals-based UI with Socket.io hot reload.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -45,6 +45,23 @@ function effective(s) {
45
45
  return order.filter((n) => want.has(n));
46
46
  }
47
47
 
48
+ // which *enabled* add-ons pull in `name` as a (transitive) dependency
49
+ function requiredBy(s, name) {
50
+ const causes = [];
51
+ for (const n of order) {
52
+ if (n === name || !s.addons[n]) continue;
53
+ const seen = new Set();
54
+ const visit = (x) => {
55
+ if (seen.has(x)) return;
56
+ seen.add(x);
57
+ (depsOf[x] || []).forEach(visit);
58
+ };
59
+ (depsOf[n] || []).forEach(visit);
60
+ if (seen.has(name)) causes.push(n);
61
+ }
62
+ return causes;
63
+ }
64
+
48
65
  const clean = (v) => String(v).replace(/[\r\n]/g, "").trim(); // one value per line; no injection
49
66
  function genEnv(s) {
50
67
  const eff = effective(s);
@@ -137,11 +154,18 @@ const field = (label, key, placeholder = "") =>
137
154
  <input class="form-control" placeholder=${placeholder} value=${() => state()[key]} oninput=${(e) => set({ [key]: e.target.value })} />
138
155
  </div>`;
139
156
 
157
+ // A dependency pulled in by another enabled add-on shows as checked + disabled
158
+ // (you can't turn it off while something needs it), with a "required by" note —
159
+ // so the .env's VOLT_ADDONS always matches what the boxes show.
140
160
  const addonRow = (a) =>
141
161
  html`<div class="form-check mb-2">
142
- <input class="form-check-input" type="checkbox" id=${"x-" + a.name} checked=${() => state().addons[a.name]} onchange=${() => toggle(a.name)} />
162
+ <input class="form-check-input" type="checkbox" id=${"x-" + a.name}
163
+ checked=${() => eff().includes(a.name)}
164
+ disabled=${() => !state().addons[a.name] && eff().includes(a.name)}
165
+ onchange=${() => toggle(a.name)} />
143
166
  <label class="form-check-label" for=${"x-" + a.name}>
144
- <span class="accent">${a.name}</span>${a.dependsOn?.length ? html` <span class="text-muted small">(needs ${a.dependsOn.join(", ")})</span>` : ""}
167
+ <span class="accent">${a.name}</span>${a.dependsOn?.length ? html` <span class="text-muted small">(needs ${a.dependsOn.join(", ")})</span>` : ""}${() =>
168
+ !state().addons[a.name] && eff().includes(a.name) ? html` <span class="text-muted small">· required by ${requiredBy(state(), a.name).join(", ")}</span>` : ""}
145
169
  <div class="small text-muted">${a.description}</div>
146
170
  </label>
147
171
  </div>`;
@@ -45,6 +45,23 @@ function effective(s) {
45
45
  return order.filter((n) => want.has(n));
46
46
  }
47
47
 
48
+ // which *enabled* add-ons pull in `name` as a (transitive) dependency
49
+ function requiredBy(s, name) {
50
+ const causes = [];
51
+ for (const n of order) {
52
+ if (n === name || !s.addons[n]) continue;
53
+ const seen = new Set();
54
+ const visit = (x) => {
55
+ if (seen.has(x)) return;
56
+ seen.add(x);
57
+ (depsOf[x] || []).forEach(visit);
58
+ };
59
+ (depsOf[n] || []).forEach(visit);
60
+ if (seen.has(name)) causes.push(n);
61
+ }
62
+ return causes;
63
+ }
64
+
48
65
  const clean = (v) => String(v).replace(/[\r\n]/g, "").trim(); // one value per line; no injection
49
66
  function genEnv(s) {
50
67
  const eff = effective(s);
@@ -137,11 +154,18 @@ const field = (label, key, placeholder = "") =>
137
154
  <input class="form-control" placeholder=${placeholder} value=${() => state()[key]} oninput=${(e) => set({ [key]: e.target.value })} />
138
155
  </div>`;
139
156
 
157
+ // A dependency pulled in by another enabled add-on shows as checked + disabled
158
+ // (you can't turn it off while something needs it), with a "required by" note —
159
+ // so the .env's VOLT_ADDONS always matches what the boxes show.
140
160
  const addonRow = (a) =>
141
161
  html`<div class="form-check mb-2">
142
- <input class="form-check-input" type="checkbox" id=${"x-" + a.name} checked=${() => state().addons[a.name]} onchange=${() => toggle(a.name)} />
162
+ <input class="form-check-input" type="checkbox" id=${"x-" + a.name}
163
+ checked=${() => eff().includes(a.name)}
164
+ disabled=${() => !state().addons[a.name] && eff().includes(a.name)}
165
+ onchange=${() => toggle(a.name)} />
143
166
  <label class="form-check-label" for=${"x-" + a.name}>
144
- <span class="accent">${a.name}</span>${a.dependsOn?.length ? html` <span class="text-muted small">(needs ${a.dependsOn.join(", ")})</span>` : ""}
167
+ <span class="accent">${a.name}</span>${a.dependsOn?.length ? html` <span class="text-muted small">(needs ${a.dependsOn.join(", ")})</span>` : ""}${() =>
168
+ !state().addons[a.name] && eff().includes(a.name) ? html` <span class="text-muted small">· required by ${requiredBy(state(), a.name).join(", ")}</span>` : ""}
145
169
  <div class="small text-muted">${a.description}</div>
146
170
  </label>
147
171
  </div>`;