@smoothbricks/cli 0.10.3 → 0.10.5

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.
Files changed (115) hide show
  1. package/README.md +9 -13
  2. package/dist/bun/preload.d.ts +13 -0
  3. package/dist/bun/preload.d.ts.map +1 -0
  4. package/dist/bun/preload.js +12 -0
  5. package/dist/cli.js +13 -5
  6. package/dist/generate/index.d.ts.map +1 -1
  7. package/dist/generate/index.js +0 -6
  8. package/dist/github-ci/index.d.ts.map +1 -1
  9. package/dist/github-ci/index.js +52 -16
  10. package/dist/github-ci/outputs.d.ts.map +1 -1
  11. package/dist/github-ci/outputs.js +1 -2
  12. package/dist/lib/cli-package.js +2 -2
  13. package/dist/lib/json.d.ts +120 -11
  14. package/dist/lib/json.d.ts.map +1 -1
  15. package/dist/lib/json.js +319 -29
  16. package/dist/lib/workspace.d.ts +7 -14
  17. package/dist/lib/workspace.d.ts.map +1 -1
  18. package/dist/lib/workspace.js +76 -82
  19. package/dist/monorepo/ci-workflow.d.ts +2 -0
  20. package/dist/monorepo/ci-workflow.d.ts.map +1 -1
  21. package/dist/monorepo/ci-workflow.js +27 -1
  22. package/dist/monorepo/git-config.js +2 -2
  23. package/dist/monorepo/index.d.ts +4 -2
  24. package/dist/monorepo/index.d.ts.map +1 -1
  25. package/dist/monorepo/index.js +19 -3
  26. package/dist/monorepo/lockfile.d.ts +14 -1
  27. package/dist/monorepo/lockfile.d.ts.map +1 -1
  28. package/dist/monorepo/lockfile.js +33 -21
  29. package/dist/monorepo/managed-files.d.ts +11 -0
  30. package/dist/monorepo/managed-files.d.ts.map +1 -1
  31. package/dist/monorepo/managed-files.js +57 -32
  32. package/dist/monorepo/package-policy.d.ts.map +1 -1
  33. package/dist/monorepo/package-policy.js +52 -45
  34. package/dist/monorepo/packed-manifest.d.ts +5 -2
  35. package/dist/monorepo/packed-manifest.d.ts.map +1 -1
  36. package/dist/monorepo/packed-manifest.js +16 -47
  37. package/dist/monorepo/packed-package.d.ts.map +1 -1
  38. package/dist/monorepo/packed-package.js +28 -16
  39. package/dist/monorepo/packs/index.d.ts.map +1 -1
  40. package/dist/monorepo/packs/index.js +14 -5
  41. package/dist/monorepo/publish-workflow.js +3 -1
  42. package/dist/monorepo/runtime.d.ts +15 -0
  43. package/dist/monorepo/runtime.d.ts.map +1 -1
  44. package/dist/monorepo/runtime.js +66 -12
  45. package/dist/monorepo/tool-validation.d.ts +2 -2
  46. package/dist/monorepo/tool-validation.d.ts.map +1 -1
  47. package/dist/monorepo/tool-validation.js +141 -38
  48. package/dist/monorepo/wrangler.d.ts.map +1 -1
  49. package/dist/monorepo/wrangler.js +13 -13
  50. package/dist/nx/index.d.ts +9 -7
  51. package/dist/nx/index.d.ts.map +1 -1
  52. package/dist/nx/index.js +48 -31
  53. package/dist/pr/index.d.ts.map +1 -1
  54. package/dist/pr/index.js +28 -32
  55. package/dist/release/candidates.d.ts +3 -2
  56. package/dist/release/candidates.d.ts.map +1 -1
  57. package/dist/release/candidates.js +4 -5
  58. package/dist/release/index.d.ts +1 -0
  59. package/dist/release/index.d.ts.map +1 -1
  60. package/dist/release/index.js +88 -59
  61. package/dist/wrangler/prepare-env.d.ts +0 -1
  62. package/dist/wrangler/prepare-env.d.ts.map +1 -1
  63. package/dist/wrangler/prepare-env.js +100 -51
  64. package/dist/wrangler/scaffold.d.ts.map +1 -1
  65. package/dist/wrangler/scaffold.js +12 -12
  66. package/managed/raw/envrc +5 -3
  67. package/managed/raw/git-format-staged.yml +19 -1
  68. package/managed/raw/patches/ttsc@0.19.3.patch +67 -0
  69. package/managed/raw/tooling/direnv/github-actions-bootstrap.sh +2 -1
  70. package/managed/raw/tooling/direnv/merge-newer-pins.sh +3 -3
  71. package/managed/raw/tooling/direnv/setup-environment.ts +170 -19
  72. package/managed/raw/tooling/git-hooks/pre-commit.sh +0 -6
  73. package/managed/templates/github/actions/cache-node-modules/action.yml +5 -2
  74. package/managed/templates/github/actions/cache-ttsc-plugins/action.yml +19 -0
  75. package/managed/templates/github/actions/setup-devenv/action.yml +5 -0
  76. package/managed/templates/github/workflows/managed-files.yml +80 -0
  77. package/package.json +15 -4
  78. package/src/bun/preload.ts +12 -0
  79. package/src/cli.ts +20 -8
  80. package/src/generate/index.ts +0 -6
  81. package/src/github-ci/index.ts +49 -18
  82. package/src/github-ci/outputs.ts +1 -2
  83. package/src/lib/cli-package.ts +2 -2
  84. package/src/lib/json.ts +293 -32
  85. package/src/lib/workspace.ts +88 -100
  86. package/src/monorepo/__tests__/ci-workflow.test.ts +3 -3
  87. package/src/monorepo/ci-workflow.ts +27 -1
  88. package/src/monorepo/git-config.ts +2 -2
  89. package/src/monorepo/index.ts +19 -3
  90. package/src/monorepo/lockfile.test.ts +35 -36
  91. package/src/monorepo/lockfile.ts +49 -24
  92. package/src/monorepo/managed-files.test.ts +1 -0
  93. package/src/monorepo/managed-files.ts +62 -38
  94. package/src/monorepo/package-policy.test.ts +20 -50
  95. package/src/monorepo/package-policy.ts +76 -59
  96. package/src/monorepo/packed-manifest.ts +18 -52
  97. package/src/monorepo/packed-package.ts +43 -22
  98. package/src/monorepo/packs/index.ts +14 -5
  99. package/src/monorepo/publish-workflow.ts +3 -1
  100. package/src/monorepo/runtime.test.ts +42 -3
  101. package/src/monorepo/runtime.ts +90 -12
  102. package/src/monorepo/tool-validation.test.ts +38 -16
  103. package/src/monorepo/tool-validation.ts +149 -41
  104. package/src/monorepo/wrangler.test.ts +11 -15
  105. package/src/monorepo/wrangler.ts +15 -15
  106. package/src/nx/index.test.ts +9 -5
  107. package/src/nx/index.ts +69 -42
  108. package/src/pr/index.ts +42 -36
  109. package/src/release/__tests__/candidates.test.ts +5 -4
  110. package/src/release/__tests__/trust-publisher.test.ts +7 -0
  111. package/src/release/candidates.ts +10 -17
  112. package/src/release/index.ts +110 -73
  113. package/src/wrangler/prepare-env.ts +75 -54
  114. package/src/wrangler/scaffold.test.ts +9 -12
  115. package/src/wrangler/scaffold.ts +13 -13
@@ -20,10 +20,78 @@ import { existsSync, readFileSync, writeFileSync } from 'node:fs';
20
20
  import { join } from 'node:path';
21
21
  import { $ } from 'bun';
22
22
  import { getStaticTOMLValue, parseTOML } from 'toml-eslint-parser';
23
- // ── runtime narrowing (wrangler JSON is external data — no casts) ─────────────
24
- export function isRecord(v) {
25
- return typeof v === 'object' && v !== null && !Array.isArray(v);
26
- }
23
+ import typia from 'typia';
24
+ const isWranglerTomlRoot = (() => {
25
+ const _ip0 = input => undefined === input["binding"] || "string" === typeof input["binding"];
26
+ const _io0 = input => undefined === input.env || "object" === typeof input.env && null !== input.env && false === Array.isArray(input.env) && _io1(input.env);
27
+ const _io1 = input => Object.keys(input).every(key => {
28
+ const value = input[key];
29
+ if (undefined === value)
30
+ return true;
31
+ return undefined === value || "object" === typeof value && null !== value && false === Array.isArray(value) && _io2(value);
32
+ });
33
+ const _io2 = input => (undefined === input.vars || "object" === typeof input.vars && null !== input.vars && false === Array.isArray(input.vars) && _io3(input.vars)) && (undefined === input.kv_namespaces || Array.isArray(input.kv_namespaces) && input.kv_namespaces.every(elem => "object" === typeof elem && null !== elem && false === Array.isArray(elem) && _io4(elem))) && (undefined === input.d1_databases || Array.isArray(input.d1_databases) && input.d1_databases.every(elem => "object" === typeof elem && null !== elem && false === Array.isArray(elem) && _io5(elem))) && Object.keys(input).every(key => {
34
+ if (["vars", "kv_namespaces", "d1_databases"].some(prop => key === prop))
35
+ return true;
36
+ const value = input[key];
37
+ if (undefined === value)
38
+ return true;
39
+ return true;
40
+ });
41
+ const _io3 = input => Object.keys(input).every(key => {
42
+ const value = input[key];
43
+ if (undefined === value)
44
+ return true;
45
+ return true;
46
+ });
47
+ const _io4 = input => _ip0(input) && (undefined === input.id || "string" === typeof input.id);
48
+ const _io5 = input => _ip0(input) && (undefined === input.database_id || "string" === typeof input.database_id) && (undefined === input.database_name || "string" === typeof input.database_name);
49
+ return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
50
+ })();
51
+ const isWranglerEnvBlock = (() => {
52
+ const _ip0 = input => undefined === input["binding"] || "string" === typeof input["binding"];
53
+ const _io0 = input => (undefined === input.vars || "object" === typeof input.vars && null !== input.vars && false === Array.isArray(input.vars) && _io1(input.vars)) && (undefined === input.kv_namespaces || Array.isArray(input.kv_namespaces) && input.kv_namespaces.every(elem => "object" === typeof elem && null !== elem && false === Array.isArray(elem) && _io2(elem))) && (undefined === input.d1_databases || Array.isArray(input.d1_databases) && input.d1_databases.every(elem => "object" === typeof elem && null !== elem && false === Array.isArray(elem) && _io3(elem))) && Object.keys(input).every(key => {
54
+ if (["vars", "kv_namespaces", "d1_databases"].some(prop => key === prop))
55
+ return true;
56
+ const value = input[key];
57
+ if (undefined === value)
58
+ return true;
59
+ return true;
60
+ });
61
+ const _io1 = input => Object.keys(input).every(key => {
62
+ const value = input[key];
63
+ if (undefined === value)
64
+ return true;
65
+ return true;
66
+ });
67
+ const _io2 = input => _ip0(input) && (undefined === input.id || "string" === typeof input.id);
68
+ const _io3 = input => _ip0(input) && (undefined === input.database_id || "string" === typeof input.database_id) && (undefined === input.database_name || "string" === typeof input.database_name);
69
+ return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
70
+ })();
71
+ const isWranglerKvRow = (() => {
72
+ const _io0 = input => (undefined === input.binding || "string" === typeof input.binding) && (undefined === input.id || "string" === typeof input.id);
73
+ return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
74
+ })();
75
+ const isWranglerD1Row = (() => {
76
+ const _io0 = input => (undefined === input.binding || "string" === typeof input.binding) && (undefined === input.database_id || "string" === typeof input.database_id) && (undefined === input.database_name || "string" === typeof input.database_name);
77
+ return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
78
+ })();
79
+ const isWranglerSecretRow = (() => {
80
+ const _io0 = input => undefined === input.name || "string" === typeof input.name;
81
+ return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
82
+ })();
83
+ const isKvNamespaceRow = (() => {
84
+ const _io0 = input => "string" === typeof input.id && "string" === typeof input.title;
85
+ return input => "object" === typeof input && null !== input && _io0(input);
86
+ })();
87
+ const isD1DatabaseRow = (() => {
88
+ const _io0 = input => "string" === typeof input.uuid && "string" === typeof input.name;
89
+ return input => "object" === typeof input && null !== input && _io0(input);
90
+ })();
91
+ const isShellErrorShape = (() => {
92
+ const _io0 = input => true && true;
93
+ return input => "object" === typeof input && null !== input && false === Array.isArray(input) && _io0(input);
94
+ })();
27
95
  // ── wrangler.toml editors (CST: read via getStaticTOMLValue, write via
28
96
  // node-range splice — comment/format-preserving by construction, no regex) ─────
29
97
  /** All top-level tables (`[env.x]`, `[env.x.vars]`, `[[env.x.d1_databases]]`, …) — each carries `resolvedKey` (incl. array index). */
@@ -33,12 +101,11 @@ function tables(toml) {
33
101
  /** Whole doc as a JS object (comments/format dropped — reads only). */
34
102
  function tomlValue(toml) {
35
103
  const value = getStaticTOMLValue(parseTOML(toml));
36
- return isRecord(value) ? value : {};
104
+ return isWranglerTomlRoot(value) ? value : {};
37
105
  }
38
106
  function envRecord(toml, env) {
39
- const envs = tomlValue(toml).env;
40
- const block = isRecord(envs) ? envs[env] : undefined;
41
- return isRecord(block) ? block : null;
107
+ const block = tomlValue(toml).env?.[env];
108
+ return isWranglerEnvBlock(block) ? block : null;
42
109
  }
43
110
  function sameKey(a, b) {
44
111
  return a.length === b.length && a.every((x, i) => x === b[i]);
@@ -54,8 +121,13 @@ function kvIn(toml, table, key) {
54
121
  /** The `<field>` KV of the `[[env.<env>.<arrayKey>]]` table whose `binding` matches, or null. */
55
122
  function arrayTableKv(toml, env, arrayKey, binding, field) {
56
123
  const block = envRecord(toml, env);
57
- const rows = isRecord(block) && Array.isArray(block[arrayKey]) ? block[arrayKey] : [];
58
- const index = rows.findIndex((r) => isRecord(r) && r.binding === binding);
124
+ const rows = Array.isArray(block?.[arrayKey]) ? block[arrayKey] : [];
125
+ const index = rows.findIndex((row) => {
126
+ if (arrayKey === 'd1_databases') {
127
+ return isWranglerD1Row(row) && row.binding === binding;
128
+ }
129
+ return isWranglerKvRow(row) && row.binding === binding;
130
+ });
59
131
  if (index < 0)
60
132
  return null;
61
133
  const table = tables(toml).find((t) => t.kind === 'array' && sameKey(t.resolvedKey, ['env', env, arrayKey, index]));
@@ -72,7 +144,7 @@ function endOfLine(toml, offset) {
72
144
  /** First `[env.<name>]` (source order; also derived from a nested `[env.<name>.*]`), or null when no env declared. */
73
145
  export function firstWranglerEnv(toml) {
74
146
  const envs = tomlValue(toml).env;
75
- if (!isRecord(envs))
147
+ if (!envs)
76
148
  return null;
77
149
  const [first] = Object.keys(envs);
78
150
  return first ?? null;
@@ -80,13 +152,11 @@ export function firstWranglerEnv(toml) {
80
152
  /** True if the toml declares `[env.<env>]` (or any `[env.<env>.*]` sub-table). */
81
153
  export function hasEnvBlock(toml, env) {
82
154
  const envs = tomlValue(toml).env;
83
- return isRecord(envs) && env in envs;
155
+ return !!envs && env in envs;
84
156
  }
85
157
  /** A `[env.<env>.vars]` value, or null if unset/empty. Reads committed public config. */
86
158
  export function getVar(toml, env, name) {
87
- const block = envRecord(toml, env);
88
- const vars = isRecord(block?.vars) ? block.vars : {};
89
- const value = vars[name];
159
+ const value = envRecord(toml, env)?.vars?.[name];
90
160
  return typeof value === 'string' && value ? value : null;
91
161
  }
92
162
  /** Set a `[env.<env>.vars]` value (JSON-encoded), preserving any trailing comment. Throws if the key is absent (scaffold first). */
@@ -99,10 +169,8 @@ export function setVar(toml, env, name, value) {
99
169
  }
100
170
  /** The id written for `[[env.<env>.kv_namespaces]]` binding, or null if absent/empty. */
101
171
  export function getKvId(toml, env, binding) {
102
- const block = envRecord(toml, env);
103
- const rows = isRecord(block) && Array.isArray(block.kv_namespaces) ? block.kv_namespaces : [];
104
- for (const row of rows) {
105
- if (isRecord(row) && row.binding === binding && typeof row.id === 'string')
172
+ for (const row of envRecord(toml, env)?.kv_namespaces ?? []) {
173
+ if (row.binding === binding && typeof row.id === 'string')
106
174
  return row.id || null;
107
175
  }
108
176
  return null;
@@ -148,20 +216,16 @@ export function blankKvIds(toml, env) {
148
216
  }
149
217
  /** The database_id written for `[[env.<env>.d1_databases]]` binding, or null if absent/empty. */
150
218
  export function getD1Id(toml, env, binding) {
151
- const block = envRecord(toml, env);
152
- const rows = isRecord(block) && Array.isArray(block.d1_databases) ? block.d1_databases : [];
153
- for (const row of rows) {
154
- if (isRecord(row) && row.binding === binding && typeof row.database_id === 'string')
219
+ for (const row of envRecord(toml, env)?.d1_databases ?? []) {
220
+ if (row.binding === binding && typeof row.database_id === 'string')
155
221
  return row.database_id || null;
156
222
  }
157
223
  return null;
158
224
  }
159
225
  /** The `database_name` for `[[env.<env>.d1_databases]]` binding, or null. Source of truth for which DB to create/reuse. */
160
226
  export function getD1Name(toml, env, binding) {
161
- const block = envRecord(toml, env);
162
- const rows = isRecord(block) && Array.isArray(block.d1_databases) ? block.d1_databases : [];
163
- for (const row of rows) {
164
- if (isRecord(row) && row.binding === binding && typeof row.database_name === 'string')
227
+ for (const row of envRecord(toml, env)?.d1_databases ?? []) {
228
+ if (row.binding === binding && typeof row.database_name === 'string')
165
229
  return row.database_name || null;
166
230
  }
167
231
  return null;
@@ -222,12 +286,9 @@ export function parseDevVarsExample(text) {
222
286
  export function readManifest(root, env) {
223
287
  const toml = readFileSync(join(root, 'wrangler.toml'), 'utf8');
224
288
  const block = envRecord(toml, env);
225
- const varsRec = isRecord(block?.vars) ? block.vars : {};
226
- const vars = Object.keys(varsRec);
227
- const kvRows = isRecord(block) && Array.isArray(block.kv_namespaces) ? block.kv_namespaces : [];
228
- const kvBindings = kvRows.flatMap((row) => (isRecord(row) && typeof row.binding === 'string' ? [row.binding] : []));
229
- const d1Rows = isRecord(block) && Array.isArray(block.d1_databases) ? block.d1_databases : [];
230
- const d1Bindings = d1Rows.flatMap((row) => (isRecord(row) && typeof row.binding === 'string' ? [row.binding] : []));
289
+ const vars = Object.keys(block?.vars ?? {});
290
+ const kvBindings = (block?.kv_namespaces ?? []).flatMap((row) => typeof row.binding === 'string' ? [row.binding] : []);
291
+ const d1Bindings = (block?.d1_databases ?? []).flatMap((row) => typeof row.binding === 'string' ? [row.binding] : []);
231
292
  const examplePath = join(root, '.dev.vars.example');
232
293
  const secrets = existsSync(examplePath) ? parseDevVarsExample(readFileSync(examplePath, 'utf8')) : [];
233
294
  return { kvBindings, d1Bindings, vars, secrets };
@@ -252,25 +313,19 @@ export function looksLikeFileSecret(name) {
252
313
  function asKvNamespaces(json) {
253
314
  if (!Array.isArray(json))
254
315
  return [];
255
- const out = [];
256
- for (const row of json) {
257
- if (isRecord(row) && typeof row.id === 'string' && typeof row.title === 'string') {
258
- out.push({ id: row.id, title: row.title });
259
- }
260
- }
261
- return out;
316
+ return json.filter(isKvNamespaceRow);
262
317
  }
263
318
  function asSecretNames(json) {
264
319
  if (!Array.isArray(json))
265
320
  return [];
266
- return json.flatMap((row) => (isRecord(row) && typeof row.name === 'string' ? [row.name] : []));
321
+ return json.flatMap((row) => (isWranglerSecretRow(row) && typeof row.name === 'string' ? [row.name] : []));
267
322
  }
268
323
  /** Best-effort ERROR line from a Bun ShellError (thrown on non-zero exit). */
269
324
  export function errText(err) {
270
- if (!isRecord(err))
325
+ if (!isShellErrorShape(err))
271
326
  return 'unknown error';
272
- const stderr = 'stderr' in err && err.stderr != null ? String(err.stderr) : '';
273
- const exit = 'exitCode' in err ? String(err.exitCode) : '?';
327
+ const stderr = err.stderr != null ? String(err.stderr) : '';
328
+ const exit = err.exitCode != null ? String(err.exitCode) : '?';
274
329
  return stderr.split('\n').find((l) => l.includes('ERROR')) ?? `exit ${exit}`;
275
330
  }
276
331
  /** Live KV namespaces on the account (`kv namespace list` emits a pure JSON array); `[]` when offline/unauthenticated. */
@@ -305,13 +360,7 @@ async function reList(logical, cwd) {
305
360
  function asD1Databases(json) {
306
361
  if (!Array.isArray(json))
307
362
  return [];
308
- const out = [];
309
- for (const row of json) {
310
- if (isRecord(row) && typeof row.uuid === 'string' && typeof row.name === 'string') {
311
- out.push({ uuid: row.uuid, name: row.name });
312
- }
313
- }
314
- return out;
363
+ return json.filter(isD1DatabaseRow);
315
364
  }
316
365
  /** Live D1 databases on the account (`d1 list --json`); `[]` when offline/unauthenticated. */
317
366
  export async function listD1Databases(cwd) {
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/wrangler/scaffold.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAsMD,4FAA4F;AAC5F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CA6B7F"}
1
+ {"version":3,"file":"scaffold.d.ts","sourceRoot":"","sources":["../../src/wrangler/scaffold.ts"],"names":[],"mappings":"AAYA,MAAM,WAAW,eAAe;IAC9B,oDAAoD;IACpD,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AAqMD,4FAA4F;AAC5F,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,eAAoB,GAAG,MAAM,CA8B7F"}
@@ -6,26 +6,25 @@
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, relative } from 'node:path';
10
- import { getOrCreateRecord, recordProperty, writeJsonObject } from '../lib/json.js';
9
+ import { join } from 'node:path';
10
+ import { ensureNx, ensureNxTargets, writeJsonObject } from '../lib/json.js';
11
11
  import { getWorkspacePackageManifests } from '../lib/workspace.js';
12
12
  /** Locate a workspace package by nx name, package name, or relative path; must have a wrangler.toml. */
13
13
  function resolveProject(root, project) {
14
14
  const manifests = getWorkspacePackageManifests(root);
15
15
  const match = manifests.find((m) => {
16
- const nx = recordProperty(m.json, 'nx');
17
- const nxName = nx && typeof nx.name === 'string' ? nx.name : null;
18
- return nxName === project || m.name === project || relative(root, m.path) === project;
16
+ const nxName = m.json.nx?.name;
17
+ return nxName === project || m.name === project || m.path === project;
19
18
  });
20
19
  if (!match) {
21
- const known = manifests.map((m) => recordProperty(m.json, 'nx')?.name ?? m.name).join(', ');
20
+ const known = manifests.map((m) => m.json.nx?.name ?? m.name).join(', ');
22
21
  throw new Error(`project "${project}" not found. Known: ${known}`);
23
22
  }
24
- const dir = match.path; // WorkspacePackageManifest.path is absolute
23
+ const dir = join(root, match.path);
25
24
  if (!existsSync(join(dir, 'wrangler.toml'))) {
26
25
  throw new Error(`"${project}" has no wrangler.toml — not a wrangler project`);
27
26
  }
28
- return { dir, label: relative(root, dir), packageJsonPath: match.packageJsonPath, json: match.json };
27
+ return { dir, label: match.path, packageJsonPath: match.packageJsonPath, json: match.json };
29
28
  }
30
29
  /** The generated starter script — generic, manifest-driven, prompt-batteries included. */
31
30
  function renderScript(projectName) {
@@ -213,13 +212,14 @@ export function scaffold(root, project, options = {}) {
213
212
  writeFileSync(scriptPath, renderScript(project));
214
213
  console.log(`${existed ? 'overwrote' : 'created'} ${label}/scripts/prepare-env.ts`);
215
214
  // Wire the nx target (idempotent).
216
- const nx = getOrCreateRecord(json, 'nx');
217
- const targets = getOrCreateRecord(nx, 'targets');
218
- if (!recordProperty(targets, 'prepare-env')) {
219
- targets['prepare-env'] = {
215
+ const nx = ensureNx(json);
216
+ const targets = ensureNxTargets(nx);
217
+ if (!targets['prepare-env']) {
218
+ const prepareEnvTarget = {
220
219
  executor: 'nx:run-commands',
221
220
  options: { command: 'bun scripts/prepare-env.ts', cwd: '{projectRoot}' },
222
221
  };
222
+ targets['prepare-env'] = prepareEnvTarget;
223
223
  writeJsonObject(packageJsonPath, json);
224
224
  console.log(`updated ${label}/package.json prepare-env target`);
225
225
  }
package/managed/raw/envrc CHANGED
@@ -11,8 +11,10 @@ esac
11
11
 
12
12
  watch_file tooling/direnv/envrc.sh
13
13
 
14
+ # Load local extensions and overrides from the project root (the gitignored
15
+ # .envrc-local); capture it before we cd into tooling/direnv, since envrc.sh
16
+ # leaves us there and a bare relative path would resolve under tooling/direnv.
17
+ local_override="$PWD/.envrc-local"
14
18
  cd tooling/direnv
15
19
  . envrc.sh
16
-
17
- # Load local extensions and overrides
18
- source_env_if_exists .envrc-local
20
+ source_env_if_exists "$local_override"
@@ -8,12 +8,16 @@ formatters:
8
8
  - '*.astro'
9
9
  - '!.*' # Exclude hidden files
10
10
 
11
- # Biome for JS/TS/JSX/TSX/JSON/HTML/CSS/GraphQL files
11
+ # Biome for JS/TS family (including CommonJS/CTS and ESM .mjs/.mts)
12
12
  biome:
13
13
  command: "biome check --files-ignore-unknown=true --use-editorconfig=true '--stdin-file-path={}' --fix"
14
14
  patterns:
15
15
  - '*.js'
16
+ - '*.cjs'
17
+ - '*.mjs'
16
18
  - '*.ts'
19
+ - '*.cts'
20
+ - '*.mts'
17
21
  - '*.jsx'
18
22
  - '*.tsx'
19
23
  - '*.json'
@@ -30,7 +34,11 @@ formatters:
30
34
  - '*'
31
35
  # Exclude files handled by Biome
32
36
  - '!*.js'
37
+ - '!*.cjs'
38
+ - '!*.mjs'
33
39
  - '!*.ts'
40
+ - '!*.cts'
41
+ - '!*.mts'
34
42
  - '!*.jsx'
35
43
  - '!*.tsx'
36
44
  - '!*.json'
@@ -38,15 +46,25 @@ formatters:
38
46
  - '!*.html'
39
47
  - '!*.css'
40
48
  - '!*.graphql'
49
+ # Exclude files handled by rustfmt
50
+ - '!*.rs'
41
51
  # Exclude files handled by Alejandra
42
52
  - '!*.nix'
43
53
  # Bun lockfiles are generated and should not be rewritten by Prettier
44
54
  - '!bun.lock'
55
+ # Cargo lockfiles are generated and should not be rewritten by Prettier
56
+ - '!Cargo.lock'
45
57
  # Binary artifacts: Prettier reads them as UTF-8 and can corrupt them
46
58
  # by replacing invalid byte sequences.
47
59
  - '!*.wasm'
48
60
  - '!*.wasm.sha256'
49
61
 
62
+ # rustfmt for Rust files
63
+ rustfmt:
64
+ command: 'rustfmt --edition 2024'
65
+ patterns:
66
+ - '*.rs'
67
+
50
68
  # Alejandra for Nix files
51
69
  alejandra:
52
70
  command: 'alejandra'
@@ -0,0 +1,67 @@
1
+ --- a/driver/emit_plugin.go
2
+ +++ b/driver/emit_plugin.go
3
+ @@ -163,6 +163,32 @@
4
+ }
5
+ host := &pluginEmitHost{program: p.TSProgram, emitResolver: p.Checker.GetEmitResolver()}
6
+ options := p.TSProgram.Options()
7
+ + // Declaration emit must observe the original program before plugin transforms
8
+ + // can mutate source-file nodes while assembling JavaScript output.
9
+ + if options.GetEmitDeclarations() {
10
+ + var wfMu sync.Mutex
11
+ + result := p.TSProgram.Emit(context.Background(), shimcompiler.EmitOptions{
12
+ + EmitOnly: shimcompiler.EmitOnlyDts,
13
+ + WriteFile: func(fileName string, text string, data *shimcompiler.WriteFileData) error {
14
+ + wfMu.Lock()
15
+ + defer wfMu.Unlock()
16
+ + if p.outputEscapesOutDir(fileName) {
17
+ + if data != nil {
18
+ + data.SkippedDtsWrite = true
19
+ + }
20
+ + return nil
21
+ + }
22
+ + if writeFile != nil {
23
+ + return writeFile(fileName, text, data)
24
+ + }
25
+ + return DefaultWriteFile(fileName, text)
26
+ + },
27
+ + })
28
+ + if result != nil && len(result.Diagnostics) != 0 {
29
+ + return convertDiagnostics(result.Diagnostics), nil
30
+ + }
31
+ + }
32
+ +
33
+ for _, sf := range shimcompiler.GetSourceFilesToEmit(host, nil, false) {
34
+ paths := shimcompiler.GetOutputPathsFor(sf, options, host, false)
35
+ if paths.JsFilePath() != "" && !p.outputEscapesOutDir(paths.JsFilePath()) {
36
+ @@ -212,31 +238,6 @@
37
+ }
38
+ }
39
+ }
40
+ - if !options.GetEmitDeclarations() {
41
+ - return nil, nil
42
+ - }
43
+ -
44
+ - var wfMu sync.Mutex
45
+ - result := p.TSProgram.Emit(context.Background(), shimcompiler.EmitOptions{
46
+ - EmitOnly: shimcompiler.EmitOnlyDts,
47
+ - WriteFile: func(fileName string, text string, data *shimcompiler.WriteFileData) error {
48
+ - wfMu.Lock()
49
+ - defer wfMu.Unlock()
50
+ - if p.outputEscapesOutDir(fileName) {
51
+ - if data != nil {
52
+ - data.SkippedDtsWrite = true
53
+ - }
54
+ - return nil
55
+ - }
56
+ - if writeFile != nil {
57
+ - return writeFile(fileName, text, data)
58
+ - }
59
+ - return DefaultWriteFile(fileName, text)
60
+ - },
61
+ - })
62
+ - if result != nil && len(result.Diagnostics) != 0 {
63
+ - return convertDiagnostics(result.Diagnostics), nil
64
+ - }
65
+ return nil, nil
66
+ }
67
+
@@ -46,10 +46,11 @@ install_devenv() {
46
46
 
47
47
  build_devenv_shell() {
48
48
  devenv shell --verbose -- date
49
- # enterShell exports this for the bootstrap itself; persist it for the
49
+ # enterShell exports these for the bootstrap itself; persist them for the
50
50
  # independent workflow step shells that run after this composite action.
51
51
  if [ -n "${GITHUB_ENV:-}" ]; then
52
52
  echo "TTSC_TSGO_BINARY=$repo_root/node_modules/@typescript/native/bin/tsc" >> "$GITHUB_ENV"
53
+ echo "TTSC_CACHE_DIR=$repo_root/.cache/ttsc" >> "$GITHUB_ENV"
53
54
  fi
54
55
  # Add repo-local tools only after the shell exists; cleanup steps use an
55
56
  # explicit PATH because failures before this point must still refresh caches.
@@ -4,9 +4,9 @@
4
4
  # Runtime version pins (nvfetcher overlay _sources/generated.{json,nix}) and the
5
5
  # devenv/flake lock (devenv.lock) frequently differ across branches or across a
6
6
  # mirror sync's `git am --3way`. Instead of stalling on a conflict, keep the
7
- # higher pin wholesale so the tree always converges to the newest runtime; the
8
- # next `devenv shell` then regenerates package.json engine/packageManager from
9
- # it (see monorepo/runtime.ts syncRootRuntimeVersions).
7
+ # higher pin wholesale so the tree always converges to the newest runtime.
8
+ # Repair package.json engine/packageManager pins explicitly with
9
+ # `smoo monorepo init --runtime-only` when needed.
10
10
  #
11
11
  # Wired by `smoo` (managed .gitattributes -> merge=smoo-newer-pins, and
12
12
  # `git config merge.smoo-newer-pins.driver` installed by applyWorkspaceGitConfig).