@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
@@ -1,8 +1,9 @@
1
1
  import { execFileSync } from 'node:child_process';
2
- import { existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
2
+ import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
3
3
  import { dirname, join, resolve } from 'node:path';
4
4
  import { fileURLToPath } from 'node:url';
5
5
  import { PLATFORM_TARGET_GLOBS } from '@smoothbricks/nx-plugin/workspace-config-policy';
6
+ import { parseNxProjectJsonText, parseStringArrayText } from '../lib/json.js';
6
7
  import { listReleasePackages, readPackageJson } from '../lib/workspace.js';
7
8
  import { renderCiWorkflowYaml } from './ci-workflow.js';
8
9
  import { renderPublishWorkflowYaml } from './publish-workflow.js';
@@ -145,6 +146,11 @@ const managedFiles = [
145
146
  target: 'tooling/direnv/merge-newer-pins.sh',
146
147
  executable: true,
147
148
  },
149
+ {
150
+ kind: 'raw',
151
+ source: 'patches/ttsc@0.19.3.patch',
152
+ target: 'patches/ttsc@0.19.3.patch',
153
+ },
148
154
  {
149
155
  kind: 'generated',
150
156
  source: 'ci-workflow',
@@ -156,6 +162,11 @@ const managedFiles = [
156
162
  target: '.github/workflows/publish.yml',
157
163
  releasePackagesOnly: true,
158
164
  },
165
+ {
166
+ kind: 'template',
167
+ source: 'github/workflows/managed-files.yml',
168
+ target: '.github/workflows/managed-files.yml',
169
+ },
159
170
  {
160
171
  kind: 'template',
161
172
  source: 'github/actions/cache-nix-devenv/action.yml',
@@ -176,6 +187,11 @@ const managedFiles = [
176
187
  source: 'github/actions/cache-node-modules/action.yml',
177
188
  target: '.github/actions/cache-node-modules/action.yml',
178
189
  },
190
+ {
191
+ kind: 'template',
192
+ source: 'github/actions/cache-ttsc-plugins/action.yml',
193
+ target: '.github/actions/cache-ttsc-plugins/action.yml',
194
+ },
179
195
  {
180
196
  kind: 'template',
181
197
  source: 'github/actions/cache-nx/action.yml',
@@ -283,8 +299,8 @@ function readResolvedNxTargetNames(root) {
283
299
  encoding: 'utf8',
284
300
  stdio: ['ignore', 'pipe', 'inherit'],
285
301
  });
286
- const projects = JSON.parse(output);
287
- if (!Array.isArray(projects) || !projects.every((project) => typeof project === 'string')) {
302
+ const projects = parseStringArrayText(output);
303
+ if (!projects) {
288
304
  return [];
289
305
  }
290
306
  const targetNames = new Set();
@@ -294,8 +310,8 @@ function readResolvedNxTargetNames(root) {
294
310
  encoding: 'utf8',
295
311
  stdio: ['ignore', 'pipe', 'inherit'],
296
312
  });
297
- const projectJson = JSON.parse(projectOutput);
298
- const targets = recordValue(recordValue(projectJson)?.targets);
313
+ const projectJson = parseNxProjectJsonText(projectOutput);
314
+ const targets = projectJson?.targets;
299
315
  if (targets) {
300
316
  for (const targetName of Object.keys(targets)) {
301
317
  targetNames.add(targetName);
@@ -316,8 +332,8 @@ function getDeployTargetInfo(root, configuration) {
316
332
  encoding: 'utf8',
317
333
  stdio: ['ignore', 'pipe', 'inherit'],
318
334
  });
319
- const projects = JSON.parse(output);
320
- if (!Array.isArray(projects) || !projects.every((project) => typeof project === 'string')) {
335
+ const projects = parseStringArrayText(output);
336
+ if (!projects) {
321
337
  return { exists: false };
322
338
  }
323
339
  let exists = false;
@@ -338,41 +354,23 @@ function nxDeployTarget(root, project, configuration) {
338
354
  encoding: 'utf8',
339
355
  stdio: ['ignore', 'pipe', 'inherit'],
340
356
  });
341
- const parsed = JSON.parse(output);
342
- const projectJson = recordValue(parsed);
343
- const targets = recordValue(projectJson?.targets);
344
- const deploy = recordValue(targets?.deploy);
345
- const configurations = recordValue(deploy?.configurations);
346
- const config = recordValue(configurations?.[configuration]);
357
+ const projectJson = parseNxProjectJsonText(output);
358
+ const deploy = projectJson?.targets?.deploy;
359
+ const config = deploy?.configurations?.[configuration];
347
360
  if (!config) {
348
361
  return { exists: false };
349
362
  }
350
- const command = deployCommand(deploy, config);
363
+ const commandValue = config.command ?? config.options?.command ?? deploy?.options?.command;
364
+ const command = typeof commandValue === 'string' ? commandValue : '';
351
365
  return { exists: true, provider: command.includes('wrangler ') ? 'cloudflare' : undefined };
352
366
  }
353
- function deployCommand(deploy, config) {
354
- const command = stringValue(config.command) ?? stringValue(recordValue(config.options)?.command);
355
- return command ?? stringValue(recordValue(deploy?.options)?.command) ?? '';
356
- }
357
- function stringValue(value) {
358
- return typeof value === 'string' ? value : undefined;
359
- }
360
367
  function getCiPushBranches(packageJson) {
361
368
  const configured = readCiPushBranches(packageJson);
362
369
  return configured.length > 0 ? configured : ['main'];
363
370
  }
364
371
  function readCiPushBranches(packageJson) {
365
- const rootPackage = recordValue(packageJson);
366
- const smoo = recordValue(rootPackage?.smoo);
367
- const github = recordValue(smoo?.github);
368
- const branches = Array.isArray(github?.pushBranches) ? github.pushBranches : [];
369
- return branches.filter((branch) => typeof branch === 'string' && branch.length > 0);
370
- }
371
- function recordValue(value) {
372
- return isRecord(value) ? value : undefined;
373
- }
374
- function isRecord(value) {
375
- return value !== null && typeof value === 'object' && !Array.isArray(value);
372
+ const branches = packageJson?.smoo?.github?.pushBranches ?? [];
373
+ return branches.filter((branch) => branch.length > 0);
376
374
  }
377
375
  function renderYamlFlowList(values) {
378
376
  return JSON.stringify(values);
@@ -395,3 +393,30 @@ export function validateManagedFiles(root) {
395
393
  }
396
394
  return failures;
397
395
  }
396
+ /**
397
+ * Non-blocking drift report: prints the per-file table and surfaces drift as
398
+ * GitHub Actions warning annotations (plain stderr elsewhere) without failing
399
+ * the run. Managed-file drift is derived state with its own remediation flow
400
+ * (the persistent managed-files PR); only `monorepo check` without --warn and
401
+ * PR-scoped gates treat it as an error.
402
+ *
403
+ * Under GitHub Actions the drifted-file count is published as the step output
404
+ * `drifted`, so downstream steps gate declaratively instead of parsing logs.
405
+ */
406
+ export function warnOnManagedFileDrift(root) {
407
+ const results = applyManagedFiles(root, 'check');
408
+ printResults(results);
409
+ const drifted = results.filter((result) => result.action === 'drifted');
410
+ if (process.env.GITHUB_OUTPUT) {
411
+ appendFileSync(process.env.GITHUB_OUTPUT, `drifted=${drifted.length}\n`);
412
+ }
413
+ if (drifted.length === 0) {
414
+ return;
415
+ }
416
+ if (process.env.GITHUB_ACTIONS === 'true') {
417
+ for (const result of drifted) {
418
+ console.log(`::warning title=Managed file drift::${result.target} drifted from the @smoothbricks/cli template; run 'smoo monorepo update'`);
419
+ }
420
+ }
421
+ console.error(`${drifted.length} managed monorepo file(s) drifted (non-blocking). Run: smoo monorepo update`);
422
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"package-policy.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-policy.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC5B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,2BAA2B,EAC3B,uBAAuB,EACxB,MAAM,+CAA+C,CAAC;AA6BvD,YAAY,EAAE,0BAA0B,IAAI,iCAAiC,EAAE,sBAAsB,EAAE,CAAC;AACxG,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,CAAC;AAYhE,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAI/D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAuB1D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMxD;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAuC7D;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,IAAI,CAwB7G;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMzD;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAoB7D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAezD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsC9D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW5D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAyB3D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcvD;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CA0DlE;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,MAAM,CAkC5G"}
1
+ {"version":3,"file":"package-policy.d.ts","sourceRoot":"","sources":["../../src/monorepo/package-policy.ts"],"names":[],"mappings":"AAKA,OAAO,EAGL,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC5B,MAAM,+CAA+C,CAAC;AACvD,OAAO,EAGL,2BAA2B,EAC3B,uBAAuB,EACxB,MAAM,+CAA+C,CAAC;AAoCvD,YAAY,EAAE,0BAA0B,IAAI,iCAAiC,EAAE,sBAAsB,EAAE,CAAC;AACxG,OAAO,EAAE,2BAA2B,EAAE,uBAAuB,EAAE,CAAC;AAYhE,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAI/D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAuB1D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMxD;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CA0C7D;AAED,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,IAAI,CAwB7G;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAMzD;AAED,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAoB7D;AAED,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC,MAAM,CAAC,CAEvE;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAczD;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAsC9D;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAW5D;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAwB3D;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAcvD;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAgElE;AAED,wBAAgB,6BAA6B,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,0BAA+B,GAAG,MAAM,CAkC5G"}
@@ -4,7 +4,7 @@ import { applyPackageTargetPolicy, checkPackageTargetPolicy, } from '@smoothbric
4
4
  import { applyReleaseConfigPolicy, checkReleaseConfigPolicy, SMOO_NX_RELEASE_TAG_PATTERN, SMOO_NX_VERSION_ACTIONS, } from '@smoothbricks/nx-plugin/release-config-policy';
5
5
  import { applyTypecheckTestPolicy, checkTypecheckTestPolicy } from '@smoothbricks/nx-plugin/typecheck-test-policy';
6
6
  import { applyWorkspaceConfigPolicy, checkWorkspaceConfigPolicy, } from '@smoothbricks/nx-plugin/workspace-config-policy';
7
- import { getOrCreateRecord, hasOwnString, isRecord, readJsonObject, recordProperty, requiredJsonObject, setMissingStringProperty, setStringProperty, stringProperty, writeJsonObject, } from '../lib/json.js';
7
+ import { ensureNx, ensurePublishConfig, ensureRepositoryObject, ensureScripts, readJsonObject, requiredJsonObject, setMissingPackageStringField, setMissingRepositoryField, setNxName, setPublishAccess, setRepositoryField, setStringProperty, writeJsonObject, } from '../lib/json.js';
8
8
  import { getWorkspacePackageManifests, getWorkspacePackages, listPackageJsonRecords, listPublicPackages, packageRepositoryInfo, repositoryInfo, sameRepositoryAfterNormalization, workspaceDependencyFields, } from '../lib/workspace.js';
9
9
  export { SMOO_NX_RELEASE_TAG_PATTERN, SMOO_NX_VERSION_ACTIONS };
10
10
  const extraCommitScopes = ['release'];
@@ -27,12 +27,12 @@ export function applyRootScriptDefaults(root) {
27
27
  if (!rootPackage) {
28
28
  return;
29
29
  }
30
- const scripts = getOrCreateRecord(rootPackage, 'scripts');
30
+ const scripts = ensureScripts(rootPackage);
31
31
  let changed = false;
32
32
  for (const [name, command] of Object.entries(rootScriptPolicy)) {
33
33
  changed = setStringProperty(scripts, name, command) || changed;
34
34
  }
35
- const nx = getOrCreateRecord(rootPackage, 'nx');
35
+ const nx = ensureNx(rootPackage);
36
36
  if (!Array.isArray(nx.includedScripts) || nx.includedScripts.length !== 0) {
37
37
  nx.includedScripts = [];
38
38
  changed = true;
@@ -56,7 +56,7 @@ export function applyNxPluginDefaults(root) {
56
56
  }
57
57
  export function applyPublicPackageDefaults(root) {
58
58
  const rootPackage = requiredJsonObject(join(root, 'package.json'));
59
- const rootLicense = stringProperty(rootPackage, 'license');
59
+ const rootLicense = rootPackage.license;
60
60
  const rootRepository = repositoryInfo(rootPackage);
61
61
  for (const pkg of listPublicPackages(root)) {
62
62
  let changed = false;
@@ -66,17 +66,19 @@ export function applyPublicPackageDefaults(root) {
66
66
  existingRepository.url === rootRepository.url &&
67
67
  rootLicense &&
68
68
  rootLicense !== 'UNLICENSED') {
69
- changed = setMissingStringProperty(pkg.json, 'license', rootLicense) || changed;
69
+ changed = setMissingPackageStringField(pkg.json, 'license', rootLicense) || changed;
70
70
  }
71
- const publishConfig = getOrCreateRecord(pkg.json, 'publishConfig');
72
- changed = setStringProperty(publishConfig, 'access', 'public') || changed;
73
- const repository = getOrCreateRecord(pkg.json, 'repository');
74
- changed =
75
- setStringProperty(repository, 'type', existingRepository?.type ?? rootRepository?.type ?? 'git') || changed;
76
- if (existingRepository && !stringProperty(repository, 'url')) {
77
- changed = setStringProperty(repository, 'url', existingRepository.url) || changed;
71
+ const publishConfig = ensurePublishConfig(pkg.json);
72
+ changed = setPublishAccess(publishConfig, 'public') || changed;
73
+ if (typeof pkg.json.repository !== 'string') {
74
+ const repository = ensureRepositoryObject(pkg.json);
75
+ changed =
76
+ setRepositoryField(repository, 'type', existingRepository?.type ?? rootRepository?.type ?? 'git') || changed;
77
+ if (existingRepository) {
78
+ changed = setMissingRepositoryField(repository, 'url', existingRepository.url) || changed;
79
+ }
80
+ changed = setRepositoryField(repository, 'directory', pkg.path.replaceAll('\\', '/')) || changed;
78
81
  }
79
- changed = setStringProperty(repository, 'directory', pkg.path.replaceAll('\\', '/')) || changed;
80
82
  changed = normalizeExportConditionOrder(pkg.json.exports) || changed;
81
83
  if (hasDevelopmentSourceExport(pkg.json.exports)) {
82
84
  changed = addFileEntry(pkg.json, 'src') || changed;
@@ -128,7 +130,7 @@ export function applyNxReleaseDefaults(root) {
128
130
  }
129
131
  export function applyNxProjectNameDefaults(root) {
130
132
  const rootPackage = requiredJsonObject(join(root, 'package.json'));
131
- const rootName = stringProperty(rootPackage, 'name');
133
+ const rootName = rootPackage.name;
132
134
  if (!rootName) {
133
135
  return;
134
136
  }
@@ -137,8 +139,8 @@ export function applyNxProjectNameDefaults(root) {
137
139
  if (!suggestedName) {
138
140
  continue;
139
141
  }
140
- const nx = getOrCreateRecord(pkg.json, 'nx');
141
- const changed = setStringProperty(nx, 'name', suggestedName);
142
+ const nx = ensureNx(pkg.json);
143
+ const changed = setNxName(nx, suggestedName);
142
144
  if (changed) {
143
145
  writeJsonObject(pkg.packageJsonPath, pkg.json);
144
146
  console.log(`updated ${pkg.path}/package.json nx.name`);
@@ -153,11 +155,10 @@ export function listValidCommitScopes(root) {
153
155
  }
154
156
  export function listNxProjectNames(root) {
155
157
  const rootPackage = readJsonObject(join(root, 'package.json'));
156
- const rootName = rootPackage ? stringProperty(rootPackage, 'name') : null;
158
+ const rootName = rootPackage?.name ?? null;
157
159
  const names = [];
158
160
  for (const pkg of getWorkspacePackageManifests(root)) {
159
- const nx = recordProperty(pkg.json, 'nx');
160
- const configuredName = nx ? stringProperty(nx, 'name') : null;
161
+ const configuredName = pkg.json.nx?.name;
161
162
  const suggestedName = rootName ? suggestNxProjectName(rootName, pkg.name) : null;
162
163
  if (configuredName) {
163
164
  names.push(configuredName);
@@ -175,11 +176,11 @@ export function validateRootPackagePolicy(root) {
175
176
  return 1;
176
177
  }
177
178
  let failures = 0;
178
- if (!stringProperty(rootPackage, 'name')) {
179
+ if (!rootPackage.name) {
179
180
  console.error('package.json must define name');
180
181
  failures++;
181
182
  }
182
- if (!stringProperty(rootPackage, 'license')) {
183
+ if (!rootPackage.license) {
183
184
  console.error('package.json must define repo-wide license');
184
185
  failures++;
185
186
  }
@@ -189,19 +190,19 @@ export function validateRootPackagePolicy(root) {
189
190
  }
190
191
  failures += validateRootScripts(rootPackage);
191
192
  failures += validateRootNxScriptInference(rootPackage);
192
- const packageManager = stringProperty(rootPackage, 'packageManager');
193
+ const packageManager = rootPackage.packageManager;
193
194
  if (!packageManager?.startsWith('bun@')) {
194
195
  console.error('package.json packageManager must use bun@<version>');
195
196
  failures++;
196
197
  }
197
198
  const bunVersion = packageManager?.startsWith('bun@') ? packageManager.slice('bun@'.length) : null;
198
- const devDependencies = recordProperty(rootPackage, 'devDependencies');
199
+ const devDependencies = rootPackage.devDependencies;
199
200
  if (!bunVersion || !devDependencies || devDependencies['@types/bun'] !== bunVersion) {
200
201
  console.error('package.json devDependencies.@types/bun must match packageManager bun version');
201
202
  failures++;
202
203
  }
203
- const engines = recordProperty(rootPackage, 'engines');
204
- if (!engines || !stringProperty(engines, 'node')) {
204
+ const engines = rootPackage.engines;
205
+ if (!engines?.node) {
205
206
  console.error('package.json engines.node must be defined');
206
207
  failures++;
207
208
  }
@@ -221,7 +222,7 @@ export function validateNxReleaseConfig(root) {
221
222
  }
222
223
  export function validateNxProjectNames(root) {
223
224
  const rootPackage = readJsonObject(join(root, 'package.json'));
224
- const rootName = rootPackage ? stringProperty(rootPackage, 'name') : null;
225
+ const rootName = rootPackage?.name ?? null;
225
226
  if (!rootName) {
226
227
  return 0;
227
228
  }
@@ -231,8 +232,7 @@ export function validateNxProjectNames(root) {
231
232
  if (!suggestedName) {
232
233
  continue;
233
234
  }
234
- const nx = recordProperty(pkg.json, 'nx');
235
- const configuredName = nx ? stringProperty(nx, 'name') : null;
235
+ const configuredName = pkg.json.nx?.name ?? null;
236
236
  if (configuredName !== suggestedName) {
237
237
  console.error(`${pkg.path}: package.json nx.name must be "${suggestedName}" so fix(${suggestedName}): maps to this project`);
238
238
  failures++;
@@ -267,16 +267,18 @@ export function validatePublicPackageMetadata(root) {
267
267
  console.error(`${pkg.path}: npm:public package must not be private`);
268
268
  failures++;
269
269
  }
270
- if (!stringProperty(pkg.json, 'license')) {
270
+ if (!pkg.json.license) {
271
271
  console.error(`${pkg.path}: public package must define license`);
272
272
  failures++;
273
273
  }
274
- const publishConfig = recordProperty(pkg.json, 'publishConfig');
275
- if (!publishConfig || stringProperty(publishConfig, 'access') !== 'public') {
274
+ const publishConfig = pkg.json.publishConfig;
275
+ if (publishConfig?.access !== 'public') {
276
276
  console.error(`${pkg.path}: public package must define publishConfig.access = public`);
277
277
  failures++;
278
278
  }
279
- const repository = recordProperty(pkg.json, 'repository');
279
+ const repository = pkg.json.repository !== null && pkg.json.repository !== undefined && typeof pkg.json.repository !== 'string'
280
+ ? pkg.json.repository
281
+ : null;
280
282
  const packageRepository = packageRepositoryInfo(pkg);
281
283
  if (!packageRepository) {
282
284
  console.error(`${pkg.path}: public package must define repository.url`);
@@ -290,11 +292,11 @@ export function validatePublicPackageMetadata(root) {
290
292
  `Use ${rootRepository.url}`);
291
293
  failures++;
292
294
  }
293
- if (!repository || !stringProperty(repository, 'type')) {
295
+ if (!repository?.type) {
294
296
  console.error(`${pkg.path}: public package must define repository.type`);
295
297
  failures++;
296
298
  }
297
- if (!repository || stringProperty(repository, 'directory') !== pkg.path.replaceAll('\\', '/')) {
299
+ if (!repository || repository.directory !== pkg.path.replaceAll('\\', '/')) {
298
300
  console.error(`${pkg.path}: public package repository.directory must be ${pkg.path.replaceAll('\\', '/')}`);
299
301
  failures++;
300
302
  }
@@ -302,11 +304,13 @@ export function validatePublicPackageMetadata(root) {
302
304
  console.error(`${pkg.path}: public package must define files`);
303
305
  failures++;
304
306
  }
305
- if (!isRecord(pkg.json.exports) && !isRecord(pkg.json.bin)) {
307
+ const hasBin = typeof pkg.json.bin === 'string' ||
308
+ (pkg.json.bin !== null && pkg.json.bin !== undefined && typeof pkg.json.bin === 'object');
309
+ if (!isPackageExportMap(pkg.json.exports) && !hasBin) {
306
310
  console.error(`${pkg.path}: public package must define exports or bin`);
307
311
  failures++;
308
312
  }
309
- if (!hasOwnString(pkg.json, 'types') && !isRecord(pkg.json.bin)) {
313
+ if (typeof pkg.json.types !== 'string' && !hasBin) {
310
314
  console.error(`${pkg.path}: public library package must define types`);
311
315
  failures++;
312
316
  }
@@ -319,7 +323,7 @@ export function validateWorkspaceDependencies(root, options = {}) {
319
323
  const workspaceNames = new Set(getWorkspacePackages(root).map((pkg) => pkg.name));
320
324
  for (const pkg of listPackageJsonRecords(root)) {
321
325
  for (const field of workspaceDependencyFields) {
322
- const dependencies = recordProperty(pkg.json, field);
326
+ const dependencies = pkg.json[field];
323
327
  if (!dependencies) {
324
328
  continue;
325
329
  }
@@ -379,7 +383,7 @@ function validateCiSkipTags(root, options) {
379
383
  function fixWorkspaceDependencyRanges(pkg, workspaceNames) {
380
384
  let changed = false;
381
385
  for (const field of workspaceDependencyFields) {
382
- const dependencies = recordProperty(pkg, field);
386
+ const dependencies = pkg[field];
383
387
  if (!dependencies) {
384
388
  continue;
385
389
  }
@@ -393,7 +397,7 @@ function fixWorkspaceDependencyRanges(pkg, workspaceNames) {
393
397
  return changed;
394
398
  }
395
399
  function validateRootScripts(rootPackage) {
396
- const scripts = recordProperty(rootPackage, 'scripts');
400
+ const scripts = rootPackage.scripts;
397
401
  let failures = 0;
398
402
  for (const [name, command] of Object.entries(rootScriptPolicy)) {
399
403
  if (scripts?.[name] !== command) {
@@ -408,8 +412,8 @@ function validateRootScripts(rootPackage) {
408
412
  return failures;
409
413
  }
410
414
  function validateRootNxScriptInference(rootPackage) {
411
- const nx = recordProperty(rootPackage, 'nx');
412
- if (nx && Array.isArray(nx.includedScripts) && nx.includedScripts.length === 0) {
415
+ const includedScripts = rootPackage.nx?.includedScripts;
416
+ if (Array.isArray(includedScripts) && includedScripts.length === 0) {
413
417
  return 0;
414
418
  }
415
419
  console.error('package.json nx.includedScripts must be [] so root scripts do not become recursive Nx targets.');
@@ -431,7 +435,7 @@ function unscopedPackageName(packageName) {
431
435
  }
432
436
  function recordKeysAreSorted(record) {
433
437
  const keys = Object.keys(record);
434
- return keys.every((key, index) => index === 0 || keys[index - 1] <= key);
438
+ return keys.every((key, index) => index === 0 || (keys[index - 1] ?? '') <= key);
435
439
  }
436
440
  function sortRecordInPlace(record) {
437
441
  if (recordKeysAreSorted(record)) {
@@ -446,8 +450,11 @@ function sortRecordInPlace(record) {
446
450
  }
447
451
  return true;
448
452
  }
453
+ function isPackageExportMap(value) {
454
+ return value !== null && value !== undefined && typeof value === 'object';
455
+ }
449
456
  function normalizeExportConditionOrder(value) {
450
- if (!isRecord(value)) {
457
+ if (!isPackageExportMap(value)) {
451
458
  return false;
452
459
  }
453
460
  let changed = false;
@@ -476,7 +483,7 @@ function normalizeExportConditionOrder(value) {
476
483
  return true;
477
484
  }
478
485
  function hasDevelopmentSourceExport(value) {
479
- if (!isRecord(value)) {
486
+ if (!isPackageExportMap(value)) {
480
487
  return false;
481
488
  }
482
489
  for (const [key, child] of Object.entries(value)) {
@@ -494,7 +501,7 @@ function addFileEntry(pkg, entry) {
494
501
  if (!Array.isArray(files) || files.includes(entry)) {
495
502
  return false;
496
503
  }
497
- const firstNegated = files.findIndex((file) => typeof file === 'string' && file.startsWith('!'));
504
+ const firstNegated = files.findIndex((file) => file.startsWith('!'));
498
505
  if (firstNegated === -1) {
499
506
  files.push(entry);
500
507
  }
@@ -1,4 +1,7 @@
1
+ import { type PackageJson } from '../lib/json.js';
1
2
  import type { PackageInfo } from '../lib/workspace.js';
2
- export declare function readPackedPackageJson(root: string, tarball: string, packageName: string): Promise<Record<string, unknown>>;
3
- export declare function validatePackedWorkspaceDependencies(root: string, sourcePackage: PackageInfo, packedPackage: Record<string, unknown>): string[];
3
+ export declare function readPackedPackageJson(root: string, tarball: string, packageName: string): Promise<PackageJson>;
4
+ export declare function validatePackedWorkspaceDependencies(root: string, sourcePackage: PackageInfo, packedPackage: PackageJson, options?: {
5
+ mode?: 'install' | 'publish';
6
+ }): string[];
4
7
  //# sourceMappingURL=packed-manifest.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"packed-manifest.d.ts","sourceRoot":"","sources":["../../src/monorepo/packed-manifest.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAGvD,wBAAsB,qBAAqB,CACzC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAUlC;AAED,wBAAgB,mCAAmC,CACjD,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,WAAW,EAC1B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GACrC,MAAM,EAAE,CA8CV"}
1
+ {"version":3,"file":"packed-manifest.d.ts","sourceRoot":"","sources":["../../src/monorepo/packed-manifest.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,WAAW,EAAwB,MAAM,gBAAgB,CAAC;AAExE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAIvD,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAUpH;AAED,wBAAgB,mCAAmC,CACjD,IAAI,EAAE,MAAM,EACZ,aAAa,EAAE,WAAW,EAC1B,aAAa,EAAE,WAAW,EAC1B,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAA;CAAO,GAC7C,MAAM,EAAE,CAyCV"}
@@ -1,32 +1,31 @@
1
- import { execSync } from 'node:child_process';
2
1
  import { $ } from 'bun';
3
- import { isRecord, recordProperty } from '../lib/json.js';
2
+ import { parsePackageJsonText } from '../lib/json.js';
4
3
  import { decode } from '../lib/run.js';
5
4
  import { getWorkspacePackages, workspaceDependencyFields } from '../lib/workspace.js';
5
+ import { publishPackDependencyVersion } from './lockfile.js';
6
6
  export async function readPackedPackageJson(root, tarball, packageName) {
7
7
  const result = await $ `tar -xOf ${tarball} package/package.json`.cwd(root).quiet().nothrow();
8
8
  if (result.exitCode !== 0) {
9
9
  throw new Error(`${packageName}: unable to inspect packed package.json.`);
10
10
  }
11
- const parsed = JSON.parse(decode(result.stdout));
12
- if (!isRecord(parsed)) {
13
- throw new Error(`${packageName}: packed package.json is not an object.`);
11
+ const parsed = parsePackageJsonText(decode(result.stdout));
12
+ if (!parsed) {
13
+ throw new Error(`${packageName}: packed package.json is invalid.`);
14
14
  }
15
15
  return parsed;
16
16
  }
17
- export function validatePackedWorkspaceDependencies(root, sourcePackage, packedPackage) {
17
+ export function validatePackedWorkspaceDependencies(root, sourcePackage, packedPackage, options = {}) {
18
+ const mode = options.mode ?? 'publish';
18
19
  const workspacePackages = getWorkspacePackages(root);
19
- const workspaceVersions = new Map(workspacePackages.map((pkg) => [pkg.name, pkg.version]));
20
- const projectNameByPackage = new Map(workspacePackages.map((pkg) => [pkg.name, pkg.projectName]));
21
20
  const failures = [];
22
21
  for (const field of workspaceDependencyFields) {
23
- const sourceDependencies = recordProperty(sourcePackage.json, field);
24
- const packedDependencies = recordProperty(packedPackage, field);
22
+ const sourceDependencies = sourcePackage.json[field];
23
+ const packedDependencies = packedPackage[field];
25
24
  if (!sourceDependencies && !packedDependencies) {
26
25
  continue;
27
26
  }
28
27
  for (const [name, range] of Object.entries(packedDependencies ?? {})) {
29
- if (typeof range === 'string' && range.startsWith('workspace:')) {
28
+ if (range.startsWith('workspace:')) {
30
29
  failures.push(`${sourcePackage.path}: packed ${field}.${name} must not contain ${range}`);
31
30
  }
32
31
  }
@@ -34,51 +33,21 @@ export function validatePackedWorkspaceDependencies(root, sourcePackage, packedP
34
33
  continue;
35
34
  }
36
35
  for (const [name, sourceRange] of Object.entries(sourceDependencies)) {
37
- const workspaceVersion = workspaceVersions.get(name);
38
- if (!workspaceVersion) {
36
+ const dep = workspacePackages.find((pkg) => pkg.name === name);
37
+ if (!dep) {
39
38
  continue;
40
39
  }
41
40
  const packedRange = packedDependencies?.[name];
42
41
  if (sourceRange !== 'workspace:*') {
43
42
  failures.push(`${sourcePackage.path}: source ${field}.${name} must use workspace:*`);
44
43
  }
45
- // When the workspace dep is at a prerelease version that was never
46
- // published, the lockfile sync rewrites it to the latest stable tag
47
- // version. Accept that version as valid.
48
- const projectName = projectNameByPackage.get(name);
49
- const expectedVersion = workspaceVersion.includes('-') && projectName
50
- ? (latestStableTagVersion(root, projectName) ?? workspaceVersion)
51
- : workspaceVersion;
44
+ // install: what day-to-day pack embeds (package.json / lockfile aligned)
45
+ // publish: what pre-publish sync embeds (unpublished -next last stable tag)
46
+ const expectedVersion = mode === 'publish' ? publishPackDependencyVersion(root, dep.projectName, dep.version) : dep.version;
52
47
  if (packedRange !== expectedVersion) {
53
- failures.push(`${sourcePackage.path}: packed ${field}.${name} must be ${expectedVersion}, got ${formatRange(packedRange)}`);
48
+ failures.push(`${sourcePackage.path}: packed ${field}.${name} must be ${expectedVersion}, got ${packedRange ?? '<missing>'}`);
54
49
  }
55
50
  }
56
51
  }
57
52
  return failures;
58
53
  }
59
- function formatRange(value) {
60
- return typeof value === 'string' ? value : '<missing>';
61
- }
62
- function latestStableTagVersion(root, projectName) {
63
- try {
64
- const output = execSync(`git tag --list '${projectName}@*' --sort=-v:refname`, {
65
- cwd: root,
66
- encoding: 'utf8',
67
- stdio: ['pipe', 'pipe', 'pipe'],
68
- });
69
- const prefix = `${projectName}@`;
70
- for (const line of output.split('\n')) {
71
- const tag = line.trim();
72
- if (!tag.startsWith(prefix))
73
- continue;
74
- const version = tag.slice(prefix.length);
75
- if (version && !version.includes('-')) {
76
- return version;
77
- }
78
- }
79
- return null;
80
- }
81
- catch {
82
- return null;
83
- }
84
- }
@@ -1 +1 @@
1
- {"version":3,"file":"packed-package.d.ts","sourceRoot":"","sources":["../../src/monorepo/packed-package.ts"],"names":[],"mappings":"AAYA,wBAAsB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMhF;AAED,wBAAsB,kCAAkC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMtF;AAED,wBAAsB,mCAAmC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMvF;AAED,wBAAsB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMpF"}
1
+ {"version":3,"file":"packed-package.d.ts","sourceRoot":"","sources":["../../src/monorepo/packed-package.ts"],"names":[],"mappings":"AAcA,wBAAsB,4BAA4B,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMhF;AAED,wBAAsB,kCAAkC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMtF;AAED,wBAAsB,mCAAmC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMvF;AAED,wBAAsB,gCAAgC,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAMpF"}