everything-dev 1.27.0 → 1.28.1

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 (114) hide show
  1. package/dist/cli/infra.cjs +1 -1
  2. package/dist/cli/infra.mjs +1 -1
  3. package/dist/cli/init.cjs +34 -9
  4. package/dist/cli/init.cjs.map +1 -1
  5. package/dist/cli/init.d.cts +2 -1
  6. package/dist/cli/init.d.cts.map +1 -1
  7. package/dist/cli/init.d.mts +2 -1
  8. package/dist/cli/init.d.mts.map +1 -1
  9. package/dist/cli/init.mjs +34 -9
  10. package/dist/cli/init.mjs.map +1 -1
  11. package/dist/cli/prompts.cjs +28 -24
  12. package/dist/cli/prompts.cjs.map +1 -1
  13. package/dist/cli/prompts.mjs +27 -24
  14. package/dist/cli/prompts.mjs.map +1 -1
  15. package/dist/cli/sync.cjs +40 -3
  16. package/dist/cli/sync.cjs.map +1 -1
  17. package/dist/cli/sync.mjs +40 -3
  18. package/dist/cli/sync.mjs.map +1 -1
  19. package/dist/cli.cjs +187 -12
  20. package/dist/cli.cjs.map +1 -1
  21. package/dist/cli.mjs +186 -11
  22. package/dist/cli.mjs.map +1 -1
  23. package/dist/config.cjs +1 -0
  24. package/dist/config.cjs.map +1 -1
  25. package/dist/config.d.cts.map +1 -1
  26. package/dist/config.d.mts.map +1 -1
  27. package/dist/config.mjs +1 -0
  28. package/dist/config.mjs.map +1 -1
  29. package/dist/contract.cjs +1 -1
  30. package/dist/contract.cjs.map +1 -1
  31. package/dist/contract.d.cts +38 -34
  32. package/dist/contract.d.cts.map +1 -1
  33. package/dist/contract.d.mts +38 -34
  34. package/dist/contract.d.mts.map +1 -1
  35. package/dist/contract.mjs +1 -0
  36. package/dist/contract.mjs.map +1 -1
  37. package/dist/dev-session.cjs +0 -1
  38. package/dist/dev-session.mjs +1 -1
  39. package/dist/index.cjs +0 -2
  40. package/dist/index.d.cts +2 -2
  41. package/dist/index.d.mts +2 -2
  42. package/dist/index.mjs +0 -1
  43. package/dist/near-cli.cjs +1 -1
  44. package/dist/near-cli.mjs +1 -1
  45. package/dist/orchestrator.cjs +1 -1
  46. package/dist/orchestrator.mjs +1 -1
  47. package/dist/plugin.cjs +183 -151
  48. package/dist/plugin.cjs.map +1 -1
  49. package/dist/plugin.d.cts +67 -34
  50. package/dist/plugin.d.cts.map +1 -1
  51. package/dist/plugin.d.mts +66 -34
  52. package/dist/plugin.d.mts.map +1 -1
  53. package/dist/plugin.mjs +173 -142
  54. package/dist/plugin.mjs.map +1 -1
  55. package/dist/service-descriptor.d.cts +34 -0
  56. package/dist/service-descriptor.d.cts.map +1 -0
  57. package/dist/service-descriptor.d.mts +36 -0
  58. package/dist/service-descriptor.d.mts.map +1 -0
  59. package/dist/types.d.cts +2 -2
  60. package/dist/types.d.mts +2 -2
  61. package/dist/utils/run.cjs +9 -20
  62. package/dist/utils/run.cjs.map +1 -1
  63. package/dist/utils/run.mjs +9 -20
  64. package/dist/utils/run.mjs.map +1 -1
  65. package/package.json +2 -2
  66. package/src/api-contract.ts +0 -623
  67. package/src/app.ts +0 -193
  68. package/src/cli/catalog.ts +0 -49
  69. package/src/cli/framework-version.ts +0 -61
  70. package/src/cli/help.ts +0 -13
  71. package/src/cli/infra.ts +0 -190
  72. package/src/cli/init.ts +0 -1145
  73. package/src/cli/parse.ts +0 -147
  74. package/src/cli/prompts.ts +0 -135
  75. package/src/cli/snapshot.ts +0 -46
  76. package/src/cli/status.ts +0 -99
  77. package/src/cli/sync.ts +0 -429
  78. package/src/cli/timing.ts +0 -63
  79. package/src/cli/upgrade.ts +0 -869
  80. package/src/cli.ts +0 -516
  81. package/src/components/dev-view.tsx +0 -352
  82. package/src/components/streaming-view.ts +0 -177
  83. package/src/config.ts +0 -893
  84. package/src/contract.meta.ts +0 -140
  85. package/src/contract.ts +0 -326
  86. package/src/dev-logs.ts +0 -92
  87. package/src/dev-session.ts +0 -283
  88. package/src/fastkv.ts +0 -181
  89. package/src/index.ts +0 -8
  90. package/src/integrity.ts +0 -138
  91. package/src/internal/manifest-normalizer.ts +0 -290
  92. package/src/merge.ts +0 -187
  93. package/src/mf.ts +0 -147
  94. package/src/near-cli.ts +0 -259
  95. package/src/network.ts +0 -3
  96. package/src/orchestrator.ts +0 -493
  97. package/src/plugin.ts +0 -1799
  98. package/src/sdk.ts +0 -14
  99. package/src/service-descriptor.ts +0 -281
  100. package/src/shared.ts +0 -249
  101. package/src/sidebar.ts +0 -140
  102. package/src/types.ts +0 -330
  103. package/src/ui/head.ts +0 -83
  104. package/src/ui/index.ts +0 -5
  105. package/src/ui/metadata.ts +0 -95
  106. package/src/ui/router.ts +0 -88
  107. package/src/ui/runtime.ts +0 -42
  108. package/src/ui/types.ts +0 -65
  109. package/src/utils/banner.ts +0 -21
  110. package/src/utils/linkify.ts +0 -11
  111. package/src/utils/path-match.ts +0 -16
  112. package/src/utils/run.ts +0 -31
  113. package/src/utils/save-config.ts +0 -20
  114. package/src/utils/theme.ts +0 -39
@@ -1,869 +0,0 @@
1
- import { existsSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
2
- import { join } from "node:path";
3
- import process from "node:process";
4
- import * as p from "@clack/prompts";
5
- import { glob } from "glob";
6
- import type { PhaseTiming, UpgradeOptions, UpgradeResult } from "../contract";
7
- import { isPlainObject as isPlainObjectFromMerge, resolveExtendsRef } from "../merge";
8
- import { saveBosConfig } from "../utils/save-config";
9
- import { readInstalledFrameworkVersion } from "./framework-version";
10
- import { fetchParentConfig, resolveSourceDir, runBunInstallForUpgrade, runTypesGen } from "./init";
11
- import { syncTemplate } from "./sync";
12
- import { timePhase } from "./timing";
13
-
14
- const FRAMEWORK_PACKAGES = ["everything-dev", "every-plugin"];
15
-
16
- const CATALOG_TOOL_PACKAGES = [
17
- "@rspack/core",
18
- "@rspack/cli",
19
- "@rsbuild/core",
20
- "@rsbuild/plugin-react",
21
- "@module-federation/enhanced",
22
- "@module-federation/node",
23
- "@module-federation/rsbuild-plugin",
24
- "@module-federation/runtime-core",
25
- "@module-federation/sdk",
26
- "@module-federation/dts-plugin",
27
- ] as const;
28
- const PINNED_CATALOG_TOOL_VERSIONS: Partial<
29
- Record<(typeof CATALOG_TOOL_PACKAGES)[number], string>
30
- > = {
31
- "@rspack/core": "1.7.11",
32
- "@rspack/cli": "1.7.11",
33
- "@rsbuild/core": "1.7.5",
34
- "@rsbuild/plugin-react": "1.4.6",
35
- };
36
- const LEGACY_UI_IMPORT_REWRITES = [
37
- ['from "@/auth"', 'from "@/app"'],
38
- ["from '@/auth'", "from '@/app'"],
39
- ['from "@/lib/use-api-client"', 'from "@/app"'],
40
- ["from '@/lib/use-api-client'", "from '@/app'"],
41
- ['from "@/lib/api-client"', 'from "@/app"'],
42
- ["from '@/lib/api-client'", "from '@/app'"],
43
- ] as const;
44
- const OBSOLETE_FILES = [
45
- "ui/src/auth.ts",
46
- "ui/src/auth-types.gen.ts",
47
- "ui/src/lib/api-client.ts",
48
- "ui/src/lib/use-api-client.ts",
49
- "ui/src/api-contract.ts",
50
- "ui/src/api-contract.gen.ts",
51
- "ui/src/lib/auth-client.ts",
52
- "ui/src/lib/session.ts",
53
- "ui/scripts/generate-metadata.ts",
54
- ".github/dependabot.yml",
55
- ".github/templates/dependabot.yml",
56
- "packages/everything-dev/cli.js",
57
- ".templatekeep",
58
- ".templatesync-exclude",
59
- ];
60
-
61
- function extractVersion(value: string | undefined): string | null {
62
- if (!value) return null;
63
- const match = value.match(/\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?/);
64
- return match?.[0] ?? null;
65
- }
66
-
67
- async function readExtendedRootCatalog(projectDir: string): Promise<Record<string, string>> {
68
- const configPath = join(projectDir, "bos.config.json");
69
- if (!existsSync(configPath)) {
70
- return {};
71
- }
72
-
73
- const localConfig = JSON.parse(readFileSync(configPath, "utf-8")) as Record<string, unknown>;
74
- let extendsRef: string | undefined;
75
- if (typeof localConfig.extends === "string") {
76
- extendsRef = localConfig.extends;
77
- } else if (isPlainObjectFromMerge(localConfig.extends)) {
78
- extendsRef = resolveExtendsRef(localConfig.extends as Record<string, string>, "production");
79
- }
80
-
81
- const parsed = extendsRef ? parseBosRef(extendsRef) : null;
82
- if (!parsed) {
83
- return {};
84
- }
85
-
86
- const { sourceDir, cleanup } = await resolveSourceDir({
87
- extendsAccount: parsed.account,
88
- extendsGateway: parsed.gateway,
89
- });
90
-
91
- try {
92
- const sourcePkgPath = join(sourceDir, "package.json");
93
- if (!existsSync(sourcePkgPath)) {
94
- return {};
95
- }
96
-
97
- const sourcePkg = JSON.parse(readFileSync(sourcePkgPath, "utf-8")) as {
98
- workspaces?: { catalog?: Record<string, string> };
99
- };
100
- return { ...(sourcePkg.workspaces?.catalog ?? {}) };
101
- } finally {
102
- await cleanup();
103
- }
104
- }
105
-
106
- function getExtendsRef(config: Record<string, unknown>): string | undefined {
107
- if (typeof config.extends === "string") {
108
- return config.extends;
109
- }
110
-
111
- if (config.extends && typeof config.extends === "object") {
112
- return resolveExtendsRef(config.extends as Record<string, string>, "production");
113
- }
114
-
115
- return undefined;
116
- }
117
-
118
- function parseBosRef(ref: string): { account: string; gateway: string } | null {
119
- const match = ref.match(/^bos:\/\/([^/]+)\/(.+)$/);
120
- if (!match?.[1] || !match[2]) return null;
121
- return { account: match[1], gateway: match[2] };
122
- }
123
-
124
- function parseTargetedRef(ref: string): { configRef: string; targetPath?: string } {
125
- const hashIndex = ref.indexOf("#");
126
- if (hashIndex === -1) {
127
- return { configRef: ref };
128
- }
129
- return {
130
- configRef: ref.slice(0, hashIndex),
131
- targetPath: ref.slice(hashIndex + 1) || undefined,
132
- };
133
- }
134
-
135
- function ensureTargetedRef(ref: string, targetPath: string): string {
136
- const parsed = parseTargetedRef(ref);
137
- if (parsed.targetPath) return ref;
138
- return `${parsed.configRef}#${targetPath}`;
139
- }
140
-
141
- function rewriteExtendsTarget(
142
- entry: Record<string, unknown> | undefined,
143
- targetPath: string,
144
- ): boolean {
145
- if (!entry?.extends) return false;
146
-
147
- if (typeof entry.extends === "string") {
148
- const next = ensureTargetedRef(entry.extends, targetPath);
149
- if (next === entry.extends) return false;
150
- entry.extends = next;
151
- return true;
152
- }
153
-
154
- if (typeof entry.extends === "object") {
155
- let changed = false;
156
- for (const [key, value] of Object.entries(entry.extends as Record<string, unknown>)) {
157
- if (typeof value !== "string") continue;
158
- const next = ensureTargetedRef(value, targetPath);
159
- if (next !== value) {
160
- (entry.extends as Record<string, unknown>)[key] = next;
161
- changed = true;
162
- }
163
- }
164
- return changed;
165
- }
166
-
167
- return false;
168
- }
169
-
170
- function migrateRootConfigTargets(config: Record<string, unknown>): boolean {
171
- let changed = false;
172
- const app =
173
- config.app && typeof config.app === "object"
174
- ? (config.app as Record<string, unknown>)
175
- : undefined;
176
-
177
- if (app?.api && typeof app.api === "object") {
178
- changed = rewriteExtendsTarget(app.api as Record<string, unknown>, "app.api") || changed;
179
- }
180
- if (app?.auth && typeof app.auth === "object") {
181
- changed = rewriteExtendsTarget(app.auth as Record<string, unknown>, "app.auth") || changed;
182
- }
183
-
184
- if (config.plugins && typeof config.plugins === "object") {
185
- for (const [pluginKey, pluginValue] of Object.entries(
186
- config.plugins as Record<string, unknown>,
187
- )) {
188
- if (typeof pluginValue === "string") {
189
- const next = ensureTargetedRef(pluginValue, `plugins.${pluginKey}`);
190
- if (next !== pluginValue) {
191
- (config.plugins as Record<string, unknown>)[pluginKey] = next;
192
- changed = true;
193
- }
194
- continue;
195
- }
196
- if (!pluginValue || typeof pluginValue !== "object") continue;
197
- changed =
198
- rewriteExtendsTarget(pluginValue as Record<string, unknown>, `plugins.${pluginKey}`) ||
199
- changed;
200
- }
201
- }
202
-
203
- return changed;
204
- }
205
-
206
- function migratePluginProviderConfig(config: Record<string, unknown>, pluginKey: string): boolean {
207
- let changed = false;
208
- if (!config.plugins || typeof config.plugins !== "object") {
209
- return false;
210
- }
211
-
212
- const plugins = config.plugins as Record<string, unknown>;
213
- const entry = plugins[pluginKey];
214
- if (!entry || typeof entry !== "object") return false;
215
-
216
- const pluginEntry = entry as Record<string, unknown>;
217
-
218
- if ("name" in pluginEntry) {
219
- delete pluginEntry.name;
220
- changed = true;
221
- }
222
-
223
- if (typeof pluginEntry.development === "string" && pluginEntry.development.startsWith("local:")) {
224
- if ("extends" in pluginEntry) {
225
- delete pluginEntry.extends;
226
- changed = true;
227
- }
228
- }
229
-
230
- changed = rewriteExtendsTarget(pluginEntry, `plugins.${pluginKey}`) || changed;
231
-
232
- return changed;
233
- }
234
-
235
- function mergePluginConfigIntoRoot(
236
- rootConfig: Record<string, unknown>,
237
- pluginKey: string,
238
- pluginConfig: Record<string, unknown>,
239
- ): boolean {
240
- let changed = false;
241
-
242
- if (!rootConfig.plugins || typeof rootConfig.plugins !== "object") {
243
- rootConfig.plugins = {};
244
- changed = true;
245
- }
246
- const plugins = rootConfig.plugins as Record<string, unknown>;
247
- if (!plugins[pluginKey] || typeof plugins[pluginKey] !== "object") {
248
- plugins[pluginKey] = {};
249
- changed = true;
250
- }
251
-
252
- const entry = plugins[pluginKey] as Record<string, unknown>;
253
-
254
- const pluginData = extractPluginEntry(pluginConfig, pluginKey);
255
-
256
- const apiData = getApiEntry(pluginConfig);
257
-
258
- if (pluginData) {
259
- for (const key of [
260
- "secrets",
261
- "variables",
262
- "routes",
263
- "sidebar",
264
- "production",
265
- "integrity",
266
- "proxy",
267
- ] as const) {
268
- if (pluginData[key] !== undefined && entry[key] === undefined) {
269
- entry[key] = pluginData[key];
270
- changed = true;
271
- }
272
- }
273
-
274
- if (typeof pluginData.development === "string" && pluginData.development.startsWith("local:")) {
275
- pluginData.development = `local:plugins/${pluginKey}`;
276
- }
277
- if (entry.development === undefined && pluginData.development !== undefined) {
278
- entry.development = pluginData.development;
279
- changed = true;
280
- }
281
- }
282
-
283
- if (apiData) {
284
- for (const key of [
285
- "production",
286
- "integrity",
287
- "proxy",
288
- "variables",
289
- "secrets",
290
- "sidebar",
291
- "routes",
292
- ] as const) {
293
- if (apiData[key] !== undefined && entry[key] === undefined) {
294
- entry[key] = apiData[key];
295
- changed = true;
296
- }
297
- }
298
- }
299
-
300
- if ("extends" in entry) {
301
- const extendsStr = typeof entry.extends === "string" ? entry.extends : undefined;
302
- if (!extendsStr || extendsStr.includes(`#plugins.${pluginKey}`)) {
303
- delete entry.extends;
304
- changed = true;
305
- }
306
- }
307
-
308
- if ("name" in entry) {
309
- delete entry.name;
310
- changed = true;
311
- }
312
-
313
- if (configHasTopLevelFields(pluginConfig, pluginKey)) {
314
- if (entry.routes === undefined && Array.isArray(pluginConfig.routes)) {
315
- entry.routes = pluginConfig.routes;
316
- changed = true;
317
- }
318
- if (entry.sidebar === undefined && Array.isArray(pluginConfig.sidebar)) {
319
- entry.sidebar = pluginConfig.sidebar;
320
- changed = true;
321
- }
322
- const api = getApiEntry(pluginConfig);
323
- if (api) {
324
- if (entry.routes === undefined && Array.isArray(api.routes)) {
325
- entry.routes = api.routes;
326
- changed = true;
327
- }
328
- if (entry.sidebar === undefined && Array.isArray(api.sidebar)) {
329
- entry.sidebar = api.sidebar;
330
- changed = true;
331
- }
332
- }
333
- }
334
-
335
- return changed;
336
- }
337
-
338
- function extractPluginEntry(
339
- pluginConfig: Record<string, unknown>,
340
- pluginKey: string,
341
- ): Record<string, unknown> | null {
342
- if (
343
- pluginConfig.plugins &&
344
- typeof pluginConfig.plugins === "object" &&
345
- (pluginConfig.plugins as Record<string, unknown>)[pluginKey] &&
346
- typeof (pluginConfig.plugins as Record<string, unknown>)[pluginKey] === "object"
347
- ) {
348
- return (pluginConfig.plugins as Record<string, unknown>)[pluginKey] as Record<string, unknown>;
349
- }
350
-
351
- const fallback: Record<string, unknown> = {};
352
- if (pluginConfig.sidebar !== undefined) {
353
- fallback.sidebar = pluginConfig.sidebar;
354
- }
355
- if (pluginConfig.routes !== undefined) {
356
- fallback.routes = pluginConfig.routes;
357
- }
358
- if (Object.keys(fallback).length > 0) {
359
- return fallback;
360
- }
361
-
362
- return null;
363
- }
364
-
365
- function configHasTopLevelFields(
366
- pluginConfig: Record<string, unknown>,
367
- _pluginKey: string,
368
- ): boolean {
369
- return (
370
- (pluginConfig.routes !== undefined && Array.isArray(pluginConfig.routes)) ||
371
- (pluginConfig.sidebar !== undefined && Array.isArray(pluginConfig.sidebar)) ||
372
- getApiEntry(pluginConfig) !== null
373
- );
374
- }
375
-
376
- function getApiEntry(pluginConfig: Record<string, unknown>): Record<string, unknown> | null {
377
- if (!pluginConfig.app || typeof pluginConfig.app !== "object") return null;
378
- const app = pluginConfig.app as Record<string, unknown>;
379
- if (!app.api || typeof app.api !== "object") return null;
380
- return app.api as Record<string, unknown>;
381
- }
382
-
383
- export async function migrateBosConfigFiles(projectDir: string): Promise<string[]> {
384
- const migrated: string[] = [];
385
- const rootConfigPath = join(projectDir, "bos.config.json");
386
-
387
- if (existsSync(rootConfigPath)) {
388
- const rootConfig = JSON.parse(readFileSync(rootConfigPath, "utf-8")) as Record<string, unknown>;
389
- let rootChanged = migrateRootConfigTargets(rootConfig);
390
-
391
- const pluginConfigPaths = await glob("plugins/*/bos.config.json", {
392
- cwd: projectDir,
393
- nodir: true,
394
- dot: false,
395
- absolute: false,
396
- });
397
-
398
- for (const relativePath of pluginConfigPaths) {
399
- const match = relativePath.match(/^plugins\/([^/]+)\/bos\.config\.json$/);
400
- const pluginKey = match?.[1];
401
- if (!pluginKey) continue;
402
-
403
- const filePath = join(projectDir, relativePath);
404
- try {
405
- const pluginConfig = JSON.parse(readFileSync(filePath, "utf-8")) as Record<string, unknown>;
406
- rootChanged = mergePluginConfigIntoRoot(rootConfig, pluginKey, pluginConfig) || rootChanged;
407
- } catch {}
408
-
409
- try {
410
- rmSync(filePath);
411
- migrated.push(relativePath);
412
- } catch {}
413
- }
414
-
415
- if (rootConfig.plugins && typeof rootConfig.plugins === "object") {
416
- for (const pluginKey of Object.keys(rootConfig.plugins as Record<string, unknown>)) {
417
- rootChanged = migratePluginProviderConfig(rootConfig, pluginKey) || rootChanged;
418
- }
419
- }
420
-
421
- if (rootChanged || migrated.length > 0) {
422
- await saveBosConfig(projectDir, rootConfig);
423
- if (!migrated.includes("bos.config.json")) {
424
- migrated.push("bos.config.json");
425
- }
426
- }
427
- }
428
-
429
- return migrated;
430
- }
431
-
432
- async function loadParentPluginOptions(projectDir: string): Promise<{
433
- localConfig: Record<string, unknown>;
434
- parentPlugins: Record<string, unknown>;
435
- newPluginKeys: string[];
436
- } | null> {
437
- const configPath = join(projectDir, "bos.config.json");
438
- if (!existsSync(configPath)) {
439
- return null;
440
- }
441
-
442
- const localConfig = JSON.parse(readFileSync(configPath, "utf-8")) as Record<string, unknown>;
443
- const extendsRef = getExtendsRef(localConfig);
444
- if (!extendsRef?.startsWith("bos://")) {
445
- return null;
446
- }
447
-
448
- const parsed = parseBosRef(extendsRef);
449
- if (!parsed) {
450
- return null;
451
- }
452
-
453
- let parentConfig: Record<string, unknown>;
454
- try {
455
- parentConfig = await fetchParentConfig(parsed.account, parsed.gateway);
456
- } catch {
457
- return null;
458
- }
459
-
460
- const parentPlugins =
461
- parentConfig.plugins && typeof parentConfig.plugins === "object"
462
- ? (parentConfig.plugins as Record<string, unknown>)
463
- : {};
464
- const localPlugins =
465
- localConfig.plugins && typeof localConfig.plugins === "object"
466
- ? (localConfig.plugins as Record<string, unknown>)
467
- : {};
468
-
469
- const newPluginKeys = Object.keys(parentPlugins).filter((key) => !(key in localPlugins));
470
- return { localConfig, parentPlugins, newPluginKeys };
471
- }
472
-
473
- async function addSelectedParentPlugins(projectDir: string): Promise<string[]> {
474
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
475
- return [];
476
- }
477
-
478
- const pluginOptions = await loadParentPluginOptions(projectDir);
479
- if (!pluginOptions || pluginOptions.newPluginKeys.length === 0) {
480
- return [];
481
- }
482
-
483
- const selectedValue = await p.multiselect({
484
- message: "Select new plugins from parent:",
485
- options: pluginOptions.newPluginKeys.map((key) => ({ value: key, label: key })),
486
- required: false,
487
- });
488
-
489
- if (p.isCancel(selectedValue)) {
490
- process.exit(0);
491
- }
492
-
493
- const selected = selectedValue as string[];
494
- if (selected.length === 0) {
495
- return [];
496
- }
497
-
498
- const localPlugins =
499
- pluginOptions.localConfig.plugins && typeof pluginOptions.localConfig.plugins === "object"
500
- ? (pluginOptions.localConfig.plugins as Record<string, unknown>)
501
- : {};
502
- const nextPlugins = { ...localPlugins };
503
- for (const key of selected) {
504
- const parentPlugin = pluginOptions.parentPlugins[key];
505
- if (parentPlugin && typeof parentPlugin === "object") {
506
- const nextPlugin = structuredClone(parentPlugin as Record<string, unknown>);
507
- rewriteExtendsTarget(nextPlugin, `plugins.${key}`);
508
- nextPlugins[key] = nextPlugin;
509
- } else if (typeof parentPlugin === "string") {
510
- nextPlugins[key] = ensureTargetedRef(parentPlugin, `plugins.${key}`);
511
- } else {
512
- nextPlugins[key] = parentPlugin;
513
- }
514
- }
515
-
516
- pluginOptions.localConfig.plugins = nextPlugins;
517
- await saveBosConfig(projectDir, pluginOptions.localConfig);
518
-
519
- return selected;
520
- }
521
-
522
- function readInstalledVersion(projectDir: string, packageName: string): string | undefined {
523
- return readInstalledFrameworkVersion(projectDir, packageName);
524
- }
525
-
526
- function setCatalogRef(field: Record<string, string> | undefined, packageName: string): boolean {
527
- if (!field || !(packageName in field)) return false;
528
- if (field[packageName] === "catalog:" || field[packageName].startsWith("file:")) return false;
529
- field[packageName] = "catalog:";
530
- return true;
531
- }
532
-
533
- function updateWorkspacePackageRefInFile(filePath: string, packageName: string): boolean {
534
- const pkg = JSON.parse(readFileSync(filePath, "utf-8")) as Record<string, unknown>;
535
- let modified = false;
536
-
537
- for (const fieldName of ["dependencies", "devDependencies", "peerDependencies"] as const) {
538
- const field = pkg[fieldName] as Record<string, string> | undefined;
539
- if (setCatalogRef(field, packageName)) {
540
- modified = true;
541
- }
542
- }
543
-
544
- if (modified) {
545
- writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\n`);
546
- }
547
- return modified;
548
- }
549
-
550
- function updateRootPackageVersion(
551
- projectDir: string,
552
- packageName: string,
553
- newVersion: string,
554
- ): boolean {
555
- const pkgPath = join(projectDir, "package.json");
556
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as Record<string, unknown>;
557
- let modified = false;
558
-
559
- for (const fieldName of ["dependencies", "devDependencies", "peerDependencies"] as const) {
560
- const field = pkg[fieldName] as Record<string, string> | undefined;
561
- if (setCatalogRef(field, packageName)) {
562
- modified = true;
563
- }
564
- }
565
-
566
- if (!pkg.workspaces || typeof pkg.workspaces !== "object") {
567
- pkg.workspaces = { packages: [], catalog: {} };
568
- modified = true;
569
- }
570
-
571
- const workspaces = pkg.workspaces as { packages?: string[]; catalog?: Record<string, string> };
572
- if (!workspaces.catalog || typeof workspaces.catalog !== "object") {
573
- workspaces.catalog = {};
574
- modified = true;
575
- }
576
-
577
- const nextVersion = newVersion;
578
- if (workspaces.catalog[packageName] !== nextVersion) {
579
- workspaces.catalog[packageName] = nextVersion;
580
- modified = true;
581
- }
582
-
583
- if (modified) {
584
- writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
585
- }
586
-
587
- return modified;
588
- }
589
-
590
- function updateRootCatalogVersion(
591
- projectDir: string,
592
- packageName: string,
593
- newVersion: string,
594
- ): boolean {
595
- const pkgPath = join(projectDir, "package.json");
596
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as Record<string, unknown>;
597
-
598
- if (!pkg.workspaces || typeof pkg.workspaces !== "object") {
599
- pkg.workspaces = { packages: [], catalog: {} };
600
- }
601
- const workspaces = pkg.workspaces as { packages?: string[]; catalog?: Record<string, string> };
602
- if (!workspaces.catalog || typeof workspaces.catalog !== "object") {
603
- workspaces.catalog = {};
604
- }
605
-
606
- const nextVersion = newVersion;
607
- if (workspaces.catalog[packageName] === nextVersion) return false;
608
-
609
- workspaces.catalog[packageName] = nextVersion;
610
- writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
611
- return true;
612
- }
613
-
614
- async function findWorkspacePackageJsons(projectDir: string): Promise<string[]> {
615
- const rootPkgPath = join(projectDir, "package.json");
616
- if (!existsSync(rootPkgPath)) return [];
617
-
618
- const rootPkg = JSON.parse(readFileSync(rootPkgPath, "utf-8")) as Record<string, unknown>;
619
- const workspaceConfig = rootPkg.workspaces as { packages?: string[] } | string[] | undefined;
620
-
621
- const patterns: string[] = [];
622
- if (Array.isArray(workspaceConfig)) {
623
- patterns.push(...workspaceConfig);
624
- } else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) {
625
- patterns.push(...workspaceConfig.packages);
626
- }
627
-
628
- if (patterns.length === 0) return [];
629
-
630
- const pkgPaths: string[] = [];
631
- for (const pattern of patterns) {
632
- const matches = await glob(pattern, { cwd: projectDir, dot: false, absolute: false });
633
- for (const match of matches) {
634
- const pkgPath = join(projectDir, match, "package.json");
635
- if (existsSync(pkgPath) && statSync(pkgPath).isFile()) {
636
- pkgPaths.push(pkgPath);
637
- }
638
- }
639
- }
640
-
641
- return [...new Set(pkgPaths)];
642
- }
643
-
644
- function buildChangelogUrl(
645
- oldVersion: string | undefined,
646
- newVersion: string,
647
- parentConfig: Record<string, unknown> | null,
648
- ): string | undefined {
649
- if (!oldVersion || oldVersion === newVersion) return undefined;
650
- const repoUrl = parentConfig?.repository as string | undefined;
651
- if (!repoUrl) return undefined;
652
-
653
- const githubMatch = repoUrl.match(/^https?:\/\/github\.com\/([^/]+)\/([^/]+?)(?:\.git)?$/);
654
- if (!githubMatch) return undefined;
655
-
656
- const [, owner, repo] = githubMatch;
657
- return `https://github.com/${owner}/${repo}/compare/v${oldVersion}...v${newVersion}`;
658
- }
659
-
660
- async function rewriteLegacyUiImports(projectDir: string): Promise<string[]> {
661
- const files = await glob("ui/src/**/*.{ts,tsx}", {
662
- cwd: projectDir,
663
- nodir: true,
664
- dot: false,
665
- absolute: false,
666
- });
667
- const migrated: string[] = [];
668
-
669
- for (const file of files) {
670
- const filePath = join(projectDir, file);
671
- const original = readFileSync(filePath, "utf-8");
672
- let next = original;
673
-
674
- for (const [from, to] of LEGACY_UI_IMPORT_REWRITES) {
675
- next = next.replaceAll(from, to);
676
- }
677
-
678
- if (next !== original) {
679
- writeFileSync(filePath, next);
680
- migrated.push(file);
681
- }
682
- }
683
-
684
- return migrated;
685
- }
686
-
687
- export async function upgradeTemplate(
688
- projectDir: string,
689
- options: UpgradeOptions,
690
- ): Promise<UpgradeResult> {
691
- const timings: PhaseTiming[] = [];
692
- const pkgPath = join(projectDir, "package.json");
693
- if (!existsSync(pkgPath)) {
694
- return {
695
- status: "error",
696
- packages: [],
697
- timings,
698
- error: "No package.json found in current directory",
699
- };
700
- }
701
-
702
- const sourceRootCatalog = await readExtendedRootCatalog(projectDir);
703
-
704
- const { packages, catalogVersionUpdates } = await timePhase(
705
- timings,
706
- "check package versions",
707
- async () => {
708
- const nextPackages: UpgradeResult["packages"] = [];
709
-
710
- for (const name of FRAMEWORK_PACKAGES) {
711
- const installed = readInstalledVersion(projectDir, name);
712
- const latest = extractVersion(sourceRootCatalog[name]);
713
-
714
- if (!latest) {
715
- nextPackages.push({ name, from: installed, to: installed ?? "unknown" });
716
- continue;
717
- }
718
-
719
- nextPackages.push({ name, from: installed, to: latest });
720
- }
721
-
722
- const nextCatalogVersionUpdates: Array<{
723
- name: string;
724
- from: string | undefined;
725
- to: string;
726
- }> = [];
727
- for (const name of CATALOG_TOOL_PACKAGES) {
728
- const installed = readInstalledVersion(projectDir, name);
729
- if (!installed) continue;
730
- const targetVersion =
731
- PINNED_CATALOG_TOOL_VERSIONS[name] ??
732
- extractVersion(sourceRootCatalog[name]) ??
733
- installed;
734
- if (installed === targetVersion) continue;
735
- nextCatalogVersionUpdates.push({ name, from: installed, to: targetVersion });
736
- }
737
-
738
- return { packages: nextPackages, catalogVersionUpdates: nextCatalogVersionUpdates };
739
- },
740
- );
741
-
742
- const hasFrameworkUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);
743
- const hasCatalogUpdates = catalogVersionUpdates.length > 0;
744
- const hasUpdates = hasFrameworkUpdates || hasCatalogUpdates;
745
-
746
- if (options.dryRun) {
747
- let changelogUrl: string | undefined;
748
- const pluginOptions = options.noSync
749
- ? null
750
- : await timePhase(timings, "discover parent plugins", () =>
751
- loadParentPluginOptions(projectDir),
752
- );
753
- if (hasUpdates) {
754
- const configPath = join(projectDir, "bos.config.json");
755
- let parentConfig: Record<string, unknown> | null = null;
756
- if (existsSync(configPath)) {
757
- try {
758
- parentConfig = JSON.parse(readFileSync(configPath, "utf-8"));
759
- } catch {}
760
- }
761
- const mainPkg = packages.find((p) => p.name === "everything-dev");
762
- if (mainPkg?.from && mainPkg.from !== mainPkg.to) {
763
- changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);
764
- }
765
- }
766
-
767
- return {
768
- status: "dry-run",
769
- packages: [
770
- ...packages,
771
- ...catalogVersionUpdates.map((u) => ({ name: u.name, from: u.from, to: u.to })),
772
- ],
773
- availablePlugins: pluginOptions?.newPluginKeys,
774
- timings,
775
- changelogUrl,
776
- };
777
- }
778
-
779
- await timePhase(timings, "apply package updates", async () => {
780
- for (const pkg of packages) {
781
- if (pkg.from !== undefined && pkg.from !== pkg.to) {
782
- updateRootPackageVersion(projectDir, pkg.name, pkg.to);
783
- }
784
- }
785
-
786
- for (const update of catalogVersionUpdates) {
787
- updateRootCatalogVersion(projectDir, update.name, update.to);
788
- }
789
-
790
- const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);
791
- for (const pkgPath of workspacePkgPaths) {
792
- for (const pkg of packages) {
793
- if (pkg.from !== undefined && pkg.from !== pkg.to) {
794
- updateWorkspacePackageRefInFile(pkgPath, pkg.name);
795
- }
796
- }
797
- for (const update of catalogVersionUpdates) {
798
- updateWorkspacePackageRefInFile(pkgPath, update.name);
799
- }
800
- }
801
- });
802
-
803
- const migratedBosConfigs = await timePhase(timings, "migrate bos configs", () =>
804
- migrateBosConfigFiles(projectDir),
805
- );
806
-
807
- let syncResult: UpgradeResult["sync"];
808
- let addedPlugins: string[] = [];
809
- if (!options.noSync) {
810
- addedPlugins = await timePhase(timings, "discover parent plugins", async () => {
811
- if (options.dryRun) return [];
812
- return addSelectedParentPlugins(projectDir);
813
- });
814
-
815
- syncResult = await timePhase(timings, "sync template", () =>
816
- syncTemplate(projectDir, {
817
- dryRun: false,
818
- force: options.force,
819
- noInstall: true,
820
- }),
821
- );
822
- }
823
-
824
- if ((hasUpdates || addedPlugins.length > 0) && !options.noInstall) {
825
- await timePhase(timings, "install dependencies", () => runBunInstallForUpgrade(projectDir));
826
- await timePhase(timings, "generate types", () => runTypesGen(projectDir));
827
- }
828
-
829
- const migratedFiles = await timePhase(timings, "clean obsolete files", async () => {
830
- const nextMigratedFiles = [
831
- ...migratedBosConfigs,
832
- ...(await rewriteLegacyUiImports(projectDir)),
833
- ];
834
- for (const file of OBSOLETE_FILES) {
835
- const filePath = join(projectDir, file);
836
- if (existsSync(filePath)) {
837
- rmSync(filePath);
838
- nextMigratedFiles.push(file);
839
- }
840
- }
841
- return nextMigratedFiles;
842
- });
843
-
844
- let changelogUrl: string | undefined;
845
- const mainPkg = packages.find((p) => p.name === "everything-dev");
846
- if (mainPkg?.from && mainPkg.from !== mainPkg.to) {
847
- const configPath = join(projectDir, "bos.config.json");
848
- let parentConfig: Record<string, unknown> | null = null;
849
- if (existsSync(configPath)) {
850
- try {
851
- parentConfig = JSON.parse(readFileSync(configPath, "utf-8"));
852
- } catch {}
853
- }
854
- changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);
855
- }
856
-
857
- return {
858
- status: "upgraded",
859
- packages: [
860
- ...packages,
861
- ...catalogVersionUpdates.map((u) => ({ name: u.name, from: u.from, to: u.to })),
862
- ],
863
- sync: syncResult,
864
- migrated: migratedFiles.length > 0 ? migratedFiles : undefined,
865
- selectedPlugins: addedPlugins.length > 0 ? addedPlugins : undefined,
866
- timings,
867
- changelogUrl,
868
- };
869
- }