@smoothbricks/cli 0.10.2 → 0.10.4
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 +31 -23
- package/dist/bun/preload.d.ts +13 -0
- package/dist/bun/preload.d.ts.map +1 -0
- package/dist/bun/preload.js +12 -0
- package/dist/cli.js +36 -4
- package/dist/generate/index.d.ts.map +1 -1
- package/dist/generate/index.js +0 -6
- package/dist/github-ci/index.d.ts +17 -2
- package/dist/github-ci/index.d.ts.map +1 -1
- package/dist/github-ci/index.js +192 -26
- package/dist/github-ci/outputs.d.ts +22 -0
- package/dist/github-ci/outputs.d.ts.map +1 -0
- package/dist/github-ci/outputs.js +648 -0
- package/dist/lib/cli-package.js +2 -2
- package/dist/lib/json.d.ts +118 -11
- package/dist/lib/json.d.ts.map +1 -1
- package/dist/lib/json.js +317 -29
- package/dist/lib/workspace.d.ts +7 -14
- package/dist/lib/workspace.d.ts.map +1 -1
- package/dist/lib/workspace.js +76 -82
- package/dist/monorepo/ci-workflow.d.ts +2 -0
- package/dist/monorepo/ci-workflow.d.ts.map +1 -1
- package/dist/monorepo/ci-workflow.js +28 -2
- package/dist/monorepo/git-config.js +2 -2
- package/dist/monorepo/index.d.ts +3 -1
- package/dist/monorepo/index.d.ts.map +1 -1
- package/dist/monorepo/index.js +6 -2
- package/dist/monorepo/lockfile.d.ts +13 -1
- package/dist/monorepo/lockfile.d.ts.map +1 -1
- package/dist/monorepo/lockfile.js +33 -21
- package/dist/monorepo/managed-files.d.ts +12 -0
- package/dist/monorepo/managed-files.d.ts.map +1 -1
- package/dist/monorepo/managed-files.js +86 -28
- package/dist/monorepo/package-policy.d.ts.map +1 -1
- package/dist/monorepo/package-policy.js +78 -45
- package/dist/monorepo/packed-manifest.d.ts +5 -2
- package/dist/monorepo/packed-manifest.d.ts.map +1 -1
- package/dist/monorepo/packed-manifest.js +16 -47
- package/dist/monorepo/packed-package.d.ts.map +1 -1
- package/dist/monorepo/packed-package.js +28 -16
- package/dist/monorepo/packs/index.d.ts +3 -0
- package/dist/monorepo/packs/index.d.ts.map +1 -1
- package/dist/monorepo/packs/index.js +18 -6
- package/dist/monorepo/publish-workflow.d.ts +1 -0
- package/dist/monorepo/publish-workflow.d.ts.map +1 -1
- package/dist/monorepo/publish-workflow.js +242 -3
- package/dist/monorepo/runtime.d.ts +15 -0
- package/dist/monorepo/runtime.d.ts.map +1 -1
- package/dist/monorepo/runtime.js +66 -12
- package/dist/monorepo/tool-validation.d.ts.map +1 -1
- package/dist/monorepo/tool-validation.js +32 -23
- package/dist/monorepo/wrangler.d.ts.map +1 -1
- package/dist/monorepo/wrangler.js +13 -13
- package/dist/nx/index.d.ts +12 -4
- package/dist/nx/index.d.ts.map +1 -1
- package/dist/nx/index.js +86 -19
- package/dist/pr/index.d.ts.map +1 -1
- package/dist/pr/index.js +28 -32
- package/dist/release/candidates.d.ts +3 -2
- package/dist/release/candidates.d.ts.map +1 -1
- package/dist/release/candidates.js +4 -5
- package/dist/release/github-release.d.ts +3 -0
- package/dist/release/github-release.d.ts.map +1 -1
- package/dist/release/github-release.js +11 -0
- package/dist/release/index.d.ts +20 -1
- package/dist/release/index.d.ts.map +1 -1
- package/dist/release/index.js +221 -89
- package/dist/release/orchestration.d.ts +8 -1
- package/dist/release/orchestration.d.ts.map +1 -1
- package/dist/release/orchestration.js +38 -1
- package/dist/wrangler/prepare-env.d.ts +0 -1
- package/dist/wrangler/prepare-env.d.ts.map +1 -1
- package/dist/wrangler/prepare-env.js +100 -51
- package/dist/wrangler/scaffold.d.ts.map +1 -1
- package/dist/wrangler/scaffold.js +12 -12
- package/managed/raw/git-format-staged.yml +19 -1
- package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +6 -0
- package/managed/raw/tooling/direnv/merge-newer-pins.sh +3 -3
- package/managed/raw/tooling/direnv/setup-environment.ts +187 -19
- package/managed/raw/tooling/git-hooks/pre-commit.sh +0 -6
- package/managed/templates/github/actions/cache-nix-devenv/action.yml +12 -10
- package/managed/templates/github/actions/cache-node-modules/action.yml +6 -2
- package/managed/templates/github/actions/cache-nx/action.yml +2 -2
- package/managed/templates/github/actions/cache-ttsc-plugins/action.yml +19 -0
- package/managed/templates/github/actions/save-nix-devenv/action.yml +11 -3
- package/managed/templates/github/actions/setup-devenv/action.yml +5 -0
- package/managed/templates/github/workflows/managed-files.yml +80 -0
- package/package.json +18 -4
- package/src/bun/preload.ts +12 -0
- package/src/cli.ts +49 -11
- package/src/generate/index.ts +0 -6
- package/src/github-ci/index.test.ts +486 -0
- package/src/github-ci/index.ts +220 -30
- package/src/github-ci/outputs.ts +505 -0
- package/src/lib/cli-package.ts +2 -2
- package/src/lib/json.ts +291 -32
- package/src/lib/workspace.ts +88 -100
- package/src/monorepo/__tests__/ci-workflow.test.ts +14 -3
- package/src/monorepo/__tests__/publish-workflow.test.ts +222 -1
- package/src/monorepo/ci-workflow.ts +28 -2
- package/src/monorepo/git-config.ts +2 -2
- package/src/monorepo/index.ts +6 -2
- package/src/monorepo/lockfile.test.ts +35 -36
- package/src/monorepo/lockfile.ts +48 -24
- package/src/monorepo/managed-files.test.ts +63 -0
- package/src/monorepo/managed-files.ts +94 -34
- package/src/monorepo/package-policy.test.ts +70 -51
- package/src/monorepo/package-policy.ts +104 -59
- package/src/monorepo/packed-manifest.ts +18 -52
- package/src/monorepo/packed-package.ts +43 -22
- package/src/monorepo/packs/index.test.ts +18 -1
- package/src/monorepo/packs/index.ts +20 -7
- package/src/monorepo/publish-workflow.ts +482 -3
- package/src/monorepo/runtime.test.ts +42 -3
- package/src/monorepo/runtime.ts +90 -12
- package/src/monorepo/tool-validation.ts +35 -25
- package/src/monorepo/wrangler.test.ts +11 -15
- package/src/monorepo/wrangler.ts +15 -15
- package/src/nx/index.test.ts +43 -0
- package/src/nx/index.ts +110 -23
- package/src/pr/index.ts +42 -36
- package/src/release/__tests__/candidates.test.ts +5 -4
- package/src/release/__tests__/fixture-repo.test.ts +18 -16
- package/src/release/__tests__/github-release.test.ts +11 -0
- package/src/release/__tests__/orchestration.test.ts +57 -2
- package/src/release/__tests__/trust-publisher.test.ts +98 -2
- package/src/release/candidates.ts +10 -17
- package/src/release/github-release.ts +12 -0
- package/src/release/index.ts +321 -112
- package/src/release/orchestration.ts +55 -3
- package/src/wrangler/prepare-env.ts +75 -54
- package/src/wrangler/scaffold.test.ts +9 -12
- package/src/wrangler/scaffold.ts +13 -13
|
@@ -30,14 +30,24 @@ export interface ReleaseNextShell<Package extends ReleasePackageInfo = ReleasePa
|
|
|
30
30
|
bumpStablePackagesToNext(packages: Package[]): Promise<void>;
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
-
export interface
|
|
34
|
-
extends ReleaseCompletionShell<Package> {
|
|
33
|
+
export interface ReleaseTargetCheckoutShell<Package extends ReleasePackageInfo = ReleasePackageInfo> {
|
|
35
34
|
checkout(ref: string): Promise<void>;
|
|
36
35
|
withDevenvEnv<T>(runWithEnv: () => Promise<T>): Promise<T>;
|
|
37
36
|
beforeRepairTarget?(target: ReleaseTarget<Package>): void;
|
|
38
37
|
afterRepairTarget?(target: ReleaseTarget<Package>): void;
|
|
39
38
|
}
|
|
40
39
|
|
|
40
|
+
export interface ReleaseRepairShell<Package extends ReleasePackageInfo = ReleasePackageInfo>
|
|
41
|
+
extends ReleaseCompletionShell<Package>,
|
|
42
|
+
ReleaseTargetCheckoutShell<Package> {
|
|
43
|
+
prepareRepairTarget?(target: ReleaseTarget<Package>): Promise<void>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface ReleaseRepairOutputsShell<Package extends ReleasePackageInfo = ReleasePackageInfo>
|
|
47
|
+
extends ReleaseTargetCheckoutShell<Package> {
|
|
48
|
+
collectRepairTargetOutputs(target: ReleaseTarget<Package>): Promise<void>;
|
|
49
|
+
}
|
|
50
|
+
|
|
41
51
|
export interface ReleaseVersionShell<Package extends ReleasePackageInfo = ReleasePackageInfo> {
|
|
42
52
|
releasePackagesAtHead(): Promise<Package[]>;
|
|
43
53
|
releaseVersionPackages(bump: string): Promise<Package[]>;
|
|
@@ -151,17 +161,59 @@ export async function repairPendingTargets<Package extends ReleasePackageInfo>(
|
|
|
151
161
|
}
|
|
152
162
|
shell.beforeRepairTarget?.(target);
|
|
153
163
|
try {
|
|
154
|
-
summaries.push(
|
|
164
|
+
summaries.push(
|
|
165
|
+
await shell.withDevenvEnv(async () => {
|
|
166
|
+
await shell.prepareRepairTarget?.(target);
|
|
167
|
+
return completeRepairTargetAtHead(shell, target, dryRun);
|
|
168
|
+
}),
|
|
169
|
+
);
|
|
155
170
|
} finally {
|
|
156
171
|
shell.afterRepairTarget?.(target);
|
|
157
172
|
}
|
|
158
173
|
}
|
|
159
174
|
} finally {
|
|
160
175
|
await shell.checkout(restoreRef);
|
|
176
|
+
if (targets.length > 0) {
|
|
177
|
+
// Loading devenv at a historical release can rebuild ignored tool outputs
|
|
178
|
+
// from that checkout. Refresh after restoring HEAD so later release phases
|
|
179
|
+
// cannot combine the current CLI with an older Nx plugin build.
|
|
180
|
+
await shell.withDevenvEnv(async () => undefined);
|
|
181
|
+
}
|
|
161
182
|
}
|
|
162
183
|
return summaries;
|
|
163
184
|
}
|
|
164
185
|
|
|
186
|
+
export async function collectRepairPlatformOutputs<Package extends ReleasePackageInfo>(
|
|
187
|
+
shell: ReleaseRepairOutputsShell<Package>,
|
|
188
|
+
targets: Array<ReleaseTarget<Package>>,
|
|
189
|
+
restoreRef: string,
|
|
190
|
+
): Promise<void> {
|
|
191
|
+
const outputTargets = targets.filter((target) => target.npmPackages.length > 0);
|
|
192
|
+
if (outputTargets.length === 0) {
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
try {
|
|
196
|
+
for (const target of outputTargets) {
|
|
197
|
+
await shell.checkout(target.sha);
|
|
198
|
+
if (target.packages.length === 0) {
|
|
199
|
+
// invariant throw: release target discovery must never yield a package-free target.
|
|
200
|
+
throw new Error(`Release commit ${target.sha} has no release packages after checkout.`);
|
|
201
|
+
}
|
|
202
|
+
shell.beforeRepairTarget?.(target);
|
|
203
|
+
try {
|
|
204
|
+
await shell.withDevenvEnv(() => shell.collectRepairTargetOutputs(target));
|
|
205
|
+
} finally {
|
|
206
|
+
shell.afterRepairTarget?.(target);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
} finally {
|
|
210
|
+
await shell.checkout(restoreRef);
|
|
211
|
+
// Historical devenv activation may rebuild ignored tool outputs. Restore the
|
|
212
|
+
// dispatch environment before the macOS job performs any further work.
|
|
213
|
+
await shell.withDevenvEnv(async () => undefined);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
165
217
|
function repairDryRunSummary<Package extends ReleasePackageInfo>(
|
|
166
218
|
target: ReleaseTarget<Package>,
|
|
167
219
|
): ReleaseSummary<Package> {
|
|
@@ -20,13 +20,50 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
|
20
20
|
import { join } from 'node:path';
|
|
21
21
|
import { $ } from 'bun';
|
|
22
22
|
import { type AST, getStaticTOMLValue, parseTOML } from 'toml-eslint-parser';
|
|
23
|
+
import typia from 'typia';
|
|
23
24
|
|
|
24
|
-
// ── runtime narrowing (wrangler JSON is external data — no casts)
|
|
25
|
+
// ── runtime narrowing (wrangler TOML/JSON is external data — no casts) ───────
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
interface WranglerTomlRoot {
|
|
28
|
+
env?: Record<string, WranglerEnvBlock | undefined>;
|
|
28
29
|
}
|
|
29
30
|
|
|
31
|
+
interface WranglerEnvBlock {
|
|
32
|
+
vars?: Record<string, unknown>;
|
|
33
|
+
kv_namespaces?: WranglerKvRow[];
|
|
34
|
+
d1_databases?: WranglerD1Row[];
|
|
35
|
+
[key: string]: unknown;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
interface WranglerKvRow {
|
|
39
|
+
binding?: string;
|
|
40
|
+
id?: string;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
interface WranglerD1Row {
|
|
44
|
+
binding?: string;
|
|
45
|
+
database_id?: string;
|
|
46
|
+
database_name?: string;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
interface WranglerSecretRow {
|
|
50
|
+
name?: string;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
interface ShellErrorShape {
|
|
54
|
+
stderr?: unknown;
|
|
55
|
+
exitCode?: unknown;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const isWranglerTomlRoot = typia.createIs<WranglerTomlRoot>();
|
|
59
|
+
const isWranglerEnvBlock = typia.createIs<WranglerEnvBlock>();
|
|
60
|
+
const isWranglerKvRow = typia.createIs<WranglerKvRow>();
|
|
61
|
+
const isWranglerD1Row = typia.createIs<WranglerD1Row>();
|
|
62
|
+
const isWranglerSecretRow = typia.createIs<WranglerSecretRow>();
|
|
63
|
+
const isKvNamespaceRow = typia.createIs<{ id: string; title: string }>();
|
|
64
|
+
const isD1DatabaseRow = typia.createIs<{ uuid: string; name: string }>();
|
|
65
|
+
const isShellErrorShape = typia.createIs<ShellErrorShape>();
|
|
66
|
+
|
|
30
67
|
// ── wrangler.toml editors (CST: read via getStaticTOMLValue, write via
|
|
31
68
|
// node-range splice — comment/format-preserving by construction, no regex) ─────
|
|
32
69
|
|
|
@@ -36,15 +73,14 @@ function tables(toml: string): AST.TOMLTable[] {
|
|
|
36
73
|
}
|
|
37
74
|
|
|
38
75
|
/** Whole doc as a JS object (comments/format dropped — reads only). */
|
|
39
|
-
function tomlValue(toml: string):
|
|
40
|
-
const value = getStaticTOMLValue(parseTOML(toml));
|
|
41
|
-
return
|
|
76
|
+
function tomlValue(toml: string): WranglerTomlRoot {
|
|
77
|
+
const value: unknown = getStaticTOMLValue(parseTOML(toml));
|
|
78
|
+
return isWranglerTomlRoot(value) ? value : {};
|
|
42
79
|
}
|
|
43
80
|
|
|
44
|
-
function envRecord(toml: string, env: string):
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
return isRecord(block) ? block : null;
|
|
81
|
+
function envRecord(toml: string, env: string): WranglerEnvBlock | null {
|
|
82
|
+
const block = tomlValue(toml).env?.[env];
|
|
83
|
+
return isWranglerEnvBlock(block) ? block : null;
|
|
48
84
|
}
|
|
49
85
|
|
|
50
86
|
function sameKey(a: (string | number)[], b: (string | number)[]): boolean {
|
|
@@ -68,8 +104,13 @@ function arrayTableKv(
|
|
|
68
104
|
field: string,
|
|
69
105
|
): AST.TOMLKeyValue | null {
|
|
70
106
|
const block = envRecord(toml, env);
|
|
71
|
-
const rows =
|
|
72
|
-
const index = rows.findIndex((
|
|
107
|
+
const rows = Array.isArray(block?.[arrayKey]) ? block[arrayKey] : [];
|
|
108
|
+
const index = rows.findIndex((row) => {
|
|
109
|
+
if (arrayKey === 'd1_databases') {
|
|
110
|
+
return isWranglerD1Row(row) && row.binding === binding;
|
|
111
|
+
}
|
|
112
|
+
return isWranglerKvRow(row) && row.binding === binding;
|
|
113
|
+
});
|
|
73
114
|
if (index < 0) return null;
|
|
74
115
|
const table = tables(toml).find((t) => t.kind === 'array' && sameKey(t.resolvedKey, ['env', env, arrayKey, index]));
|
|
75
116
|
return table ? kvIn(toml, table, field) : null;
|
|
@@ -88,7 +129,7 @@ function endOfLine(toml: string, offset: number): number {
|
|
|
88
129
|
/** First `[env.<name>]` (source order; also derived from a nested `[env.<name>.*]`), or null when no env declared. */
|
|
89
130
|
export function firstWranglerEnv(toml: string): string | null {
|
|
90
131
|
const envs = tomlValue(toml).env;
|
|
91
|
-
if (!
|
|
132
|
+
if (!envs) return null;
|
|
92
133
|
const [first] = Object.keys(envs);
|
|
93
134
|
return first ?? null;
|
|
94
135
|
}
|
|
@@ -96,14 +137,12 @@ export function firstWranglerEnv(toml: string): string | null {
|
|
|
96
137
|
/** True if the toml declares `[env.<env>]` (or any `[env.<env>.*]` sub-table). */
|
|
97
138
|
export function hasEnvBlock(toml: string, env: string): boolean {
|
|
98
139
|
const envs = tomlValue(toml).env;
|
|
99
|
-
return
|
|
140
|
+
return !!envs && env in envs;
|
|
100
141
|
}
|
|
101
142
|
|
|
102
143
|
/** A `[env.<env>.vars]` value, or null if unset/empty. Reads committed public config. */
|
|
103
144
|
export function getVar(toml: string, env: string, name: string): string | null {
|
|
104
|
-
const
|
|
105
|
-
const vars = isRecord(block?.vars) ? block.vars : {};
|
|
106
|
-
const value = vars[name];
|
|
145
|
+
const value = envRecord(toml, env)?.vars?.[name];
|
|
107
146
|
return typeof value === 'string' && value ? value : null;
|
|
108
147
|
}
|
|
109
148
|
|
|
@@ -117,10 +156,8 @@ export function setVar(toml: string, env: string, name: string, value: string):
|
|
|
117
156
|
|
|
118
157
|
/** The id written for `[[env.<env>.kv_namespaces]]` binding, or null if absent/empty. */
|
|
119
158
|
export function getKvId(toml: string, env: string, binding: string): string | null {
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
for (const row of rows) {
|
|
123
|
-
if (isRecord(row) && row.binding === binding && typeof row.id === 'string') return row.id || null;
|
|
159
|
+
for (const row of envRecord(toml, env)?.kv_namespaces ?? []) {
|
|
160
|
+
if (row.binding === binding && typeof row.id === 'string') return row.id || null;
|
|
124
161
|
}
|
|
125
162
|
return null;
|
|
126
163
|
}
|
|
@@ -166,21 +203,16 @@ export function blankKvIds(toml: string, env: string): string {
|
|
|
166
203
|
|
|
167
204
|
/** The database_id written for `[[env.<env>.d1_databases]]` binding, or null if absent/empty. */
|
|
168
205
|
export function getD1Id(toml: string, env: string, binding: string): string | null {
|
|
169
|
-
const
|
|
170
|
-
|
|
171
|
-
for (const row of rows) {
|
|
172
|
-
if (isRecord(row) && row.binding === binding && typeof row.database_id === 'string') return row.database_id || null;
|
|
206
|
+
for (const row of envRecord(toml, env)?.d1_databases ?? []) {
|
|
207
|
+
if (row.binding === binding && typeof row.database_id === 'string') return row.database_id || null;
|
|
173
208
|
}
|
|
174
209
|
return null;
|
|
175
210
|
}
|
|
176
211
|
|
|
177
212
|
/** The `database_name` for `[[env.<env>.d1_databases]]` binding, or null. Source of truth for which DB to create/reuse. */
|
|
178
213
|
export function getD1Name(toml: string, env: string, binding: string): string | null {
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
for (const row of rows) {
|
|
182
|
-
if (isRecord(row) && row.binding === binding && typeof row.database_name === 'string')
|
|
183
|
-
return row.database_name || null;
|
|
214
|
+
for (const row of envRecord(toml, env)?.d1_databases ?? []) {
|
|
215
|
+
if (row.binding === binding && typeof row.database_name === 'string') return row.database_name || null;
|
|
184
216
|
}
|
|
185
217
|
return null;
|
|
186
218
|
}
|
|
@@ -260,12 +292,13 @@ export function parseDevVarsExample(text: string): string[] {
|
|
|
260
292
|
export function readManifest(root: string, env: string): Manifest {
|
|
261
293
|
const toml = readFileSync(join(root, 'wrangler.toml'), 'utf8');
|
|
262
294
|
const block = envRecord(toml, env);
|
|
263
|
-
const
|
|
264
|
-
const
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
295
|
+
const vars = Object.keys(block?.vars ?? {});
|
|
296
|
+
const kvBindings = (block?.kv_namespaces ?? []).flatMap((row) =>
|
|
297
|
+
typeof row.binding === 'string' ? [row.binding] : [],
|
|
298
|
+
);
|
|
299
|
+
const d1Bindings = (block?.d1_databases ?? []).flatMap((row) =>
|
|
300
|
+
typeof row.binding === 'string' ? [row.binding] : [],
|
|
301
|
+
);
|
|
269
302
|
const examplePath = join(root, '.dev.vars.example');
|
|
270
303
|
const secrets = existsSync(examplePath) ? parseDevVarsExample(readFileSync(examplePath, 'utf8')) : [];
|
|
271
304
|
return { kvBindings, d1Bindings, vars, secrets };
|
|
@@ -300,25 +333,19 @@ export interface KvNamespace {
|
|
|
300
333
|
|
|
301
334
|
function asKvNamespaces(json: unknown): KvNamespace[] {
|
|
302
335
|
if (!Array.isArray(json)) return [];
|
|
303
|
-
|
|
304
|
-
for (const row of json) {
|
|
305
|
-
if (isRecord(row) && typeof row.id === 'string' && typeof row.title === 'string') {
|
|
306
|
-
out.push({ id: row.id, title: row.title });
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
return out;
|
|
336
|
+
return json.filter(isKvNamespaceRow);
|
|
310
337
|
}
|
|
311
338
|
|
|
312
339
|
function asSecretNames(json: unknown): string[] {
|
|
313
340
|
if (!Array.isArray(json)) return [];
|
|
314
|
-
return json.flatMap((row) => (
|
|
341
|
+
return json.flatMap((row) => (isWranglerSecretRow(row) && typeof row.name === 'string' ? [row.name] : []));
|
|
315
342
|
}
|
|
316
343
|
|
|
317
344
|
/** Best-effort ERROR line from a Bun ShellError (thrown on non-zero exit). */
|
|
318
345
|
export function errText(err: unknown): string {
|
|
319
|
-
if (!
|
|
320
|
-
const stderr =
|
|
321
|
-
const exit =
|
|
346
|
+
if (!isShellErrorShape(err)) return 'unknown error';
|
|
347
|
+
const stderr = err.stderr != null ? String(err.stderr) : '';
|
|
348
|
+
const exit = err.exitCode != null ? String(err.exitCode) : '?';
|
|
322
349
|
return stderr.split('\n').find((l) => l.includes('ERROR')) ?? `exit ${exit}`;
|
|
323
350
|
}
|
|
324
351
|
|
|
@@ -358,13 +385,7 @@ export interface D1Database {
|
|
|
358
385
|
|
|
359
386
|
function asD1Databases(json: unknown): D1Database[] {
|
|
360
387
|
if (!Array.isArray(json)) return [];
|
|
361
|
-
|
|
362
|
-
for (const row of json) {
|
|
363
|
-
if (isRecord(row) && typeof row.uuid === 'string' && typeof row.name === 'string') {
|
|
364
|
-
out.push({ uuid: row.uuid, name: row.name });
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
return out;
|
|
388
|
+
return json.filter(isD1DatabaseRow);
|
|
368
389
|
}
|
|
369
390
|
|
|
370
391
|
/** Live D1 databases on the account (`d1 list --json`); `[]` when offline/unauthenticated. */
|
|
@@ -2,6 +2,7 @@ import { describe, expect, it, spyOn } from 'bun:test';
|
|
|
2
2
|
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { tmpdir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
+
import { type PackageJson, parsePackageJsonText } from '../lib/json.js';
|
|
5
6
|
import { scaffold } from './scaffold.js';
|
|
6
7
|
|
|
7
8
|
describe('scaffold', () => {
|
|
@@ -164,27 +165,23 @@ async function createWorkspace(
|
|
|
164
165
|
return root;
|
|
165
166
|
}
|
|
166
167
|
|
|
167
|
-
function nxTargets(pkg:
|
|
168
|
-
const
|
|
169
|
-
if (!
|
|
168
|
+
function nxTargets(pkg: PackageJson): NonNullable<NonNullable<PackageJson['nx']>['targets']> {
|
|
169
|
+
const targets = pkg.nx?.targets;
|
|
170
|
+
if (!targets) {
|
|
170
171
|
throw new Error('nx.targets not found');
|
|
171
172
|
}
|
|
172
|
-
return
|
|
173
|
+
return targets;
|
|
173
174
|
}
|
|
174
175
|
|
|
175
|
-
function
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
async function readJson(path: string): Promise<Record<string, unknown>> {
|
|
180
|
-
const parsed: unknown = JSON.parse(await readFile(path, 'utf8'));
|
|
181
|
-
if (!isRecord(parsed)) {
|
|
176
|
+
async function readJson(path: string): Promise<PackageJson> {
|
|
177
|
+
const parsed = parsePackageJsonText(await readFile(path, 'utf8'));
|
|
178
|
+
if (!parsed) {
|
|
182
179
|
throw new Error('expected JSON object');
|
|
183
180
|
}
|
|
184
181
|
return parsed;
|
|
185
182
|
}
|
|
186
183
|
|
|
187
|
-
async function writeJson(path: string, value:
|
|
184
|
+
async function writeJson(path: string, value: PackageJson): Promise<void> {
|
|
188
185
|
await mkdir(join(path, '..'), { recursive: true });
|
|
189
186
|
await writeFile(path, `${JSON.stringify(value, null, 2)}\n`);
|
|
190
187
|
}
|
package/src/wrangler/scaffold.ts
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* .dev.vars.example), so it never drifts from what the worker declares.
|
|
7
7
|
*/
|
|
8
8
|
import { existsSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
9
|
-
import { join
|
|
10
|
-
import {
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import { ensureNx, ensureNxTargets, type NxTargetConfig, type PackageJson, writeJsonObject } from '../lib/json.js';
|
|
11
11
|
import { getWorkspacePackageManifests } from '../lib/workspace.js';
|
|
12
12
|
|
|
13
13
|
export interface ScaffoldOptions {
|
|
@@ -19,22 +19,21 @@ export interface ScaffoldOptions {
|
|
|
19
19
|
function resolveProject(
|
|
20
20
|
root: string,
|
|
21
21
|
project: string,
|
|
22
|
-
): { dir: string; label: string; packageJsonPath: string; json:
|
|
22
|
+
): { dir: string; label: string; packageJsonPath: string; json: PackageJson } {
|
|
23
23
|
const manifests = getWorkspacePackageManifests(root);
|
|
24
24
|
const match = manifests.find((m) => {
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
return nxName === project || m.name === project || relative(root, m.path) === project;
|
|
25
|
+
const nxName = m.json.nx?.name;
|
|
26
|
+
return nxName === project || m.name === project || m.path === project;
|
|
28
27
|
});
|
|
29
28
|
if (!match) {
|
|
30
|
-
const known = manifests.map((m) =>
|
|
29
|
+
const known = manifests.map((m) => m.json.nx?.name ?? m.name).join(', ');
|
|
31
30
|
throw new Error(`project "${project}" not found. Known: ${known}`);
|
|
32
31
|
}
|
|
33
|
-
const dir = match.path;
|
|
32
|
+
const dir = join(root, match.path);
|
|
34
33
|
if (!existsSync(join(dir, 'wrangler.toml'))) {
|
|
35
34
|
throw new Error(`"${project}" has no wrangler.toml — not a wrangler project`);
|
|
36
35
|
}
|
|
37
|
-
return { dir, label:
|
|
36
|
+
return { dir, label: match.path, packageJsonPath: match.packageJsonPath, json: match.json };
|
|
38
37
|
}
|
|
39
38
|
|
|
40
39
|
/** The generated starter script — generic, manifest-driven, prompt-batteries included. */
|
|
@@ -226,13 +225,14 @@ export function scaffold(root: string, project: string, options: ScaffoldOptions
|
|
|
226
225
|
console.log(`${existed ? 'overwrote' : 'created'} ${label}/scripts/prepare-env.ts`);
|
|
227
226
|
|
|
228
227
|
// Wire the nx target (idempotent).
|
|
229
|
-
const nx =
|
|
230
|
-
const targets =
|
|
231
|
-
if (!
|
|
232
|
-
|
|
228
|
+
const nx = ensureNx(json);
|
|
229
|
+
const targets = ensureNxTargets(nx);
|
|
230
|
+
if (!targets['prepare-env']) {
|
|
231
|
+
const prepareEnvTarget: NxTargetConfig = {
|
|
233
232
|
executor: 'nx:run-commands',
|
|
234
233
|
options: { command: 'bun scripts/prepare-env.ts', cwd: '{projectRoot}' },
|
|
235
234
|
};
|
|
235
|
+
targets['prepare-env'] = prepareEnvTarget;
|
|
236
236
|
writeJsonObject(packageJsonPath, json);
|
|
237
237
|
console.log(`updated ${label}/package.json prepare-env target`);
|
|
238
238
|
}
|