everything-dev 1.7.0 → 1.7.2

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.
@@ -3,6 +3,7 @@ const require_cli_init = require('./init.cjs');
3
3
  const require_sync = require('./sync.cjs');
4
4
  let node_fs = require("node:fs");
5
5
  let node_path = require("node:path");
6
+ let glob = require("glob");
6
7
 
7
8
  //#region src/cli/upgrade.ts
8
9
  const FRAMEWORK_PACKAGES = ["everything-dev", "every-plugin"];
@@ -28,18 +29,75 @@ function readInstalledVersion(projectDir, packageName) {
28
29
  if (!version) return void 0;
29
30
  return version.replace(/^[\^~>=]+/, "");
30
31
  }
31
- function updatePackageVersion(projectDir, packageName, newVersion) {
32
- const pkgPath = (0, node_path.join)(projectDir, "package.json");
33
- const pkg = JSON.parse((0, node_fs.readFileSync)(pkgPath, "utf-8"));
34
- if (pkg.dependencies && typeof pkg.dependencies === "object") {
35
- const deps = pkg.dependencies;
36
- if (deps[packageName] !== void 0) deps[packageName] = `^${newVersion}`;
32
+ function isBumpedableVersion(value) {
33
+ if (!value) return false;
34
+ if (value === "workspace:*") return false;
35
+ if (value.startsWith("catalog:")) return false;
36
+ return true;
37
+ }
38
+ function bumpDepField(field, packageName, newVersion) {
39
+ if (!field) return false;
40
+ if (!(packageName in field)) return false;
41
+ const current = field[packageName];
42
+ if (!isBumpedableVersion(current)) return false;
43
+ field[packageName] = `^${newVersion}`;
44
+ return true;
45
+ }
46
+ function bumpCatalog(catalog, packageName, newVersion) {
47
+ if (!catalog) return false;
48
+ if (!(packageName in catalog)) return false;
49
+ const current = catalog[packageName];
50
+ if (!isBumpedableVersion(current)) return false;
51
+ catalog[packageName] = `^${newVersion}`;
52
+ return true;
53
+ }
54
+ function bumpPackageJson(pkg, packageName, newVersion) {
55
+ const fields = [];
56
+ for (const fieldName of [
57
+ "dependencies",
58
+ "devDependencies",
59
+ "peerDependencies"
60
+ ]) {
61
+ const field = pkg[fieldName];
62
+ if (bumpDepField(field, packageName, newVersion)) fields.push(fieldName);
37
63
  }
38
- if (pkg.devDependencies && typeof pkg.devDependencies === "object") {
39
- const deps = pkg.devDependencies;
40
- if (deps[packageName] !== void 0) deps[packageName] = `^${newVersion}`;
64
+ const workspaces = pkg.workspaces;
65
+ if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) fields.push("workspaces.catalog");
66
+ return {
67
+ modified: fields.length > 0,
68
+ fields
69
+ };
70
+ }
71
+ function updatePackageVersionInFile(filePath, packageName, newVersion) {
72
+ const pkg = JSON.parse((0, node_fs.readFileSync)(filePath, "utf-8"));
73
+ const result = bumpPackageJson(pkg, packageName, newVersion);
74
+ if (result.modified) (0, node_fs.writeFileSync)(filePath, `${JSON.stringify(pkg, null, 2)}\n`);
75
+ return result.modified;
76
+ }
77
+ function updatePackageVersion(projectDir, packageName, newVersion) {
78
+ return updatePackageVersionInFile((0, node_path.join)(projectDir, "package.json"), packageName, newVersion);
79
+ }
80
+ async function findWorkspacePackageJsons(projectDir) {
81
+ const rootPkgPath = (0, node_path.join)(projectDir, "package.json");
82
+ if (!(0, node_fs.existsSync)(rootPkgPath)) return [];
83
+ const workspaceConfig = JSON.parse((0, node_fs.readFileSync)(rootPkgPath, "utf-8")).workspaces;
84
+ const patterns = [];
85
+ if (Array.isArray(workspaceConfig)) patterns.push(...workspaceConfig);
86
+ else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) patterns.push(...workspaceConfig.packages);
87
+ if (patterns.length === 0) return [];
88
+ const pkgPaths = [];
89
+ for (const pattern of patterns) {
90
+ const matches = await (0, glob.glob)(pattern, {
91
+ cwd: projectDir,
92
+ dot: false,
93
+ absolute: false
94
+ });
95
+ for (const match of matches) {
96
+ const pkgPath = (0, node_path.join)(projectDir, match, "package.json");
97
+ if ((0, node_fs.existsSync)(pkgPath) && (0, node_fs.statSync)(pkgPath).isFile()) pkgPaths.push(pkgPath);
98
+ }
41
99
  }
42
- (0, node_fs.writeFileSync)(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
100
+ return [...new Set(pkgPaths)];
43
101
  }
44
102
  function buildChangelogUrl(oldVersion, newVersion, parentConfig) {
45
103
  if (!oldVersion || oldVersion === newVersion) return void 0;
@@ -93,6 +151,8 @@ async function upgradeTemplate(projectDir, options) {
93
151
  };
94
152
  }
95
153
  for (const pkg of packages) if (pkg.from !== void 0 && pkg.from !== pkg.to) updatePackageVersion(projectDir, pkg.name, pkg.to);
154
+ const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);
155
+ for (const pkgPath of workspacePkgPaths) for (const pkg of packages) if (pkg.from !== void 0 && pkg.from !== pkg.to) updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);
96
156
  if (hasUpdates && !options.noInstall) await require_cli_init.runBunInstall(projectDir);
97
157
  let syncResult;
98
158
  if (!options.noSync) syncResult = await require_sync.syncTemplate(projectDir, {
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.cjs","names":["runBunInstall","syncTemplate"],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { UpgradeOptions, UpgradeResult } from \"../contract\";\nimport { runBunInstall } from \"./init\";\nimport { syncTemplate } from \"./sync\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\ninterface NpmPackageInfo {\n version: string;\n}\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as NpmPackageInfo;\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction updatePackageVersion(projectDir: string, packageName: string, newVersion: string): void {\n const pkgPath = join(projectDir, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n\n if (pkg.dependencies && typeof pkg.dependencies === \"object\") {\n const deps = pkg.dependencies as Record<string, string>;\n if (deps[packageName] !== undefined) {\n deps[packageName] = `^${newVersion}`;\n }\n }\n\n if (pkg.devDependencies && typeof pkg.devDependencies === \"object\") {\n const deps = pkg.devDependencies as Record<string, string>;\n if (deps[packageName] !== undefined) {\n deps[packageName] = `^${newVersion}`;\n }\n }\n\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n}\n\nfunction buildChangelogUrl(\n oldVersion: string | undefined,\n newVersion: string,\n parentConfig: Record<string, unknown> | null,\n): string | undefined {\n if (!oldVersion || oldVersion === newVersion) return undefined;\n const repoUrl = parentConfig?.repository as string | undefined;\n if (!repoUrl) return undefined;\n\n const githubMatch = repoUrl.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (!githubMatch) return undefined;\n\n const [, owner, repo] = githubMatch;\n return `https://github.com/${owner}/${repo}/compare/v${oldVersion}...v${newVersion}`;\n}\n\nexport async function upgradeTemplate(\n projectDir: string,\n options: UpgradeOptions,\n): Promise<UpgradeResult> {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return {\n status: \"error\",\n packages: [],\n error: \"No package.json found in current directory\",\n };\n }\n\n const packages: UpgradeResult[\"packages\"] = [];\n\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n\n if (!latest) {\n packages.push({ name, from: installed, to: installed ?? \"unknown\" });\n continue;\n }\n\n packages.push({ name, from: installed, to: latest });\n }\n\n const hasUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);\n\n if (options.dryRun) {\n let changelogUrl: string | undefined;\n if (hasUpdates) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n }\n\n return {\n status: \"dry-run\",\n packages,\n changelogUrl,\n };\n }\n\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersion(projectDir, pkg.name, pkg.to);\n }\n }\n\n if (hasUpdates && !options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n let syncResult: UpgradeResult[\"sync\"];\n if (!options.noSync) {\n syncResult = await syncTemplate(projectDir, {\n dryRun: false,\n force: options.force,\n noInstall: true,\n });\n }\n\n let changelogUrl: string | undefined;\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n\n return {\n status: \"upgraded\",\n packages,\n sync: syncResult,\n changelogUrl,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"upgrade.cjs","names":["runBunInstall","syncTemplate"],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { UpgradeOptions, UpgradeResult } from \"../contract\";\nimport { runBunInstall } from \"./init\";\nimport { syncTemplate } from \"./sync\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\ninterface NpmPackageInfo {\n version: string;\n}\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as NpmPackageInfo;\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction isBumpedableVersion(value: string | undefined): boolean {\n if (!value) return false;\n if (value === \"workspace:*\") return false;\n if (value.startsWith(\"catalog:\")) return false;\n return true;\n}\n\nfunction bumpDepField(\n field: Record<string, string> | undefined,\n packageName: string,\n newVersion: string,\n): boolean {\n if (!field) return false;\n if (!(packageName in field)) return false;\n const current = field[packageName];\n if (!isBumpedableVersion(current)) return false;\n field[packageName] = `^${newVersion}`;\n return true;\n}\n\nfunction bumpCatalog(catalog: Record<string, string> | undefined, packageName: string, newVersion: string): boolean {\n if (!catalog) return false;\n if (!(packageName in catalog)) return false;\n const current = catalog[packageName];\n if (!isBumpedableVersion(current)) return false;\n catalog[packageName] = `^${newVersion}`;\n return true;\n}\n\ninterface BumpResult {\n modified: boolean;\n fields: string[];\n}\n\nfunction bumpPackageJson(pkg: Record<string, unknown>, packageName: string, newVersion: string): BumpResult {\n const fields: string[] = [];\n\n for (const fieldName of [\"dependencies\", \"devDependencies\", \"peerDependencies\"] as const) {\n const field = pkg[fieldName] as Record<string, string> | undefined;\n if (bumpDepField(field, packageName, newVersion)) {\n fields.push(fieldName);\n }\n }\n\n const workspaces = pkg.workspaces as { catalog?: Record<string, string> } | undefined;\n if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) {\n fields.push(\"workspaces.catalog\");\n }\n\n return { modified: fields.length > 0, fields };\n}\n\nfunction updatePackageVersionInFile(filePath: string, packageName: string, newVersion: string): boolean {\n const pkg = JSON.parse(readFileSync(filePath, \"utf-8\")) as Record<string, unknown>;\n const result = bumpPackageJson(pkg, packageName, newVersion);\n if (result.modified) {\n writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n return result.modified;\n}\n\nfunction updatePackageVersion(projectDir: string, packageName: string, newVersion: string): boolean {\n return updatePackageVersionInFile(join(projectDir, \"package.json\"), packageName, newVersion);\n}\n\nasync function findWorkspacePackageJsons(projectDir: string): Promise<string[]> {\n const rootPkgPath = join(projectDir, \"package.json\");\n if (!existsSync(rootPkgPath)) return [];\n\n const rootPkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n const workspaceConfig = rootPkg.workspaces as { packages?: string[] } | string[] | undefined;\n\n const patterns: string[] = [];\n if (Array.isArray(workspaceConfig)) {\n patterns.push(...workspaceConfig);\n } else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) {\n patterns.push(...workspaceConfig.packages);\n }\n\n if (patterns.length === 0) return [];\n\n const pkgPaths: string[] = [];\n for (const pattern of patterns) {\n const matches = await glob(pattern, { cwd: projectDir, dot: false, absolute: false });\n for (const match of matches) {\n const pkgPath = join(projectDir, match, \"package.json\");\n if (existsSync(pkgPath) && statSync(pkgPath).isFile()) {\n pkgPaths.push(pkgPath);\n }\n }\n }\n\n return [...new Set(pkgPaths)];\n}\n\nfunction buildChangelogUrl(\n oldVersion: string | undefined,\n newVersion: string,\n parentConfig: Record<string, unknown> | null,\n): string | undefined {\n if (!oldVersion || oldVersion === newVersion) return undefined;\n const repoUrl = parentConfig?.repository as string | undefined;\n if (!repoUrl) return undefined;\n\n const githubMatch = repoUrl.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (!githubMatch) return undefined;\n\n const [, owner, repo] = githubMatch;\n return `https://github.com/${owner}/${repo}/compare/v${oldVersion}...v${newVersion}`;\n}\n\nexport async function upgradeTemplate(\n projectDir: string,\n options: UpgradeOptions,\n): Promise<UpgradeResult> {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return {\n status: \"error\",\n packages: [],\n error: \"No package.json found in current directory\",\n };\n }\n\n const packages: UpgradeResult[\"packages\"] = [];\n\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n\n if (!latest) {\n packages.push({ name, from: installed, to: installed ?? \"unknown\" });\n continue;\n }\n\n packages.push({ name, from: installed, to: latest });\n }\n\n const hasUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);\n\n if (options.dryRun) {\n let changelogUrl: string | undefined;\n if (hasUpdates) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n }\n\n return {\n status: \"dry-run\",\n packages,\n changelogUrl,\n };\n }\n\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersion(projectDir, pkg.name, pkg.to);\n }\n }\n\n const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);\n for (const pkgPath of workspacePkgPaths) {\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);\n }\n }\n }\n\n if (hasUpdates && !options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n let syncResult: UpgradeResult[\"sync\"];\n if (!options.noSync) {\n syncResult = await syncTemplate(projectDir, {\n dryRun: false,\n force: options.force,\n noInstall: true,\n });\n }\n\n let changelogUrl: string | undefined;\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n\n return {\n status: \"upgraded\",\n packages,\n sync: syncResult,\n changelogUrl,\n };\n}\n"],"mappings":"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"}
@@ -1,7 +1,8 @@
1
1
  import { runBunInstall } from "./init.mjs";
2
2
  import { syncTemplate } from "./sync.mjs";
3
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
3
+ import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
4
4
  import { join } from "node:path";
5
+ import { glob } from "glob";
5
6
 
6
7
  //#region src/cli/upgrade.ts
7
8
  const FRAMEWORK_PACKAGES = ["everything-dev", "every-plugin"];
@@ -27,18 +28,75 @@ function readInstalledVersion(projectDir, packageName) {
27
28
  if (!version) return void 0;
28
29
  return version.replace(/^[\^~>=]+/, "");
29
30
  }
30
- function updatePackageVersion(projectDir, packageName, newVersion) {
31
- const pkgPath = join(projectDir, "package.json");
32
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
33
- if (pkg.dependencies && typeof pkg.dependencies === "object") {
34
- const deps = pkg.dependencies;
35
- if (deps[packageName] !== void 0) deps[packageName] = `^${newVersion}`;
31
+ function isBumpedableVersion(value) {
32
+ if (!value) return false;
33
+ if (value === "workspace:*") return false;
34
+ if (value.startsWith("catalog:")) return false;
35
+ return true;
36
+ }
37
+ function bumpDepField(field, packageName, newVersion) {
38
+ if (!field) return false;
39
+ if (!(packageName in field)) return false;
40
+ const current = field[packageName];
41
+ if (!isBumpedableVersion(current)) return false;
42
+ field[packageName] = `^${newVersion}`;
43
+ return true;
44
+ }
45
+ function bumpCatalog(catalog, packageName, newVersion) {
46
+ if (!catalog) return false;
47
+ if (!(packageName in catalog)) return false;
48
+ const current = catalog[packageName];
49
+ if (!isBumpedableVersion(current)) return false;
50
+ catalog[packageName] = `^${newVersion}`;
51
+ return true;
52
+ }
53
+ function bumpPackageJson(pkg, packageName, newVersion) {
54
+ const fields = [];
55
+ for (const fieldName of [
56
+ "dependencies",
57
+ "devDependencies",
58
+ "peerDependencies"
59
+ ]) {
60
+ const field = pkg[fieldName];
61
+ if (bumpDepField(field, packageName, newVersion)) fields.push(fieldName);
36
62
  }
37
- if (pkg.devDependencies && typeof pkg.devDependencies === "object") {
38
- const deps = pkg.devDependencies;
39
- if (deps[packageName] !== void 0) deps[packageName] = `^${newVersion}`;
63
+ const workspaces = pkg.workspaces;
64
+ if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) fields.push("workspaces.catalog");
65
+ return {
66
+ modified: fields.length > 0,
67
+ fields
68
+ };
69
+ }
70
+ function updatePackageVersionInFile(filePath, packageName, newVersion) {
71
+ const pkg = JSON.parse(readFileSync(filePath, "utf-8"));
72
+ const result = bumpPackageJson(pkg, packageName, newVersion);
73
+ if (result.modified) writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\n`);
74
+ return result.modified;
75
+ }
76
+ function updatePackageVersion(projectDir, packageName, newVersion) {
77
+ return updatePackageVersionInFile(join(projectDir, "package.json"), packageName, newVersion);
78
+ }
79
+ async function findWorkspacePackageJsons(projectDir) {
80
+ const rootPkgPath = join(projectDir, "package.json");
81
+ if (!existsSync(rootPkgPath)) return [];
82
+ const workspaceConfig = JSON.parse(readFileSync(rootPkgPath, "utf-8")).workspaces;
83
+ const patterns = [];
84
+ if (Array.isArray(workspaceConfig)) patterns.push(...workspaceConfig);
85
+ else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) patterns.push(...workspaceConfig.packages);
86
+ if (patterns.length === 0) return [];
87
+ const pkgPaths = [];
88
+ for (const pattern of patterns) {
89
+ const matches = await glob(pattern, {
90
+ cwd: projectDir,
91
+ dot: false,
92
+ absolute: false
93
+ });
94
+ for (const match of matches) {
95
+ const pkgPath = join(projectDir, match, "package.json");
96
+ if (existsSync(pkgPath) && statSync(pkgPath).isFile()) pkgPaths.push(pkgPath);
97
+ }
40
98
  }
41
- writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
99
+ return [...new Set(pkgPaths)];
42
100
  }
43
101
  function buildChangelogUrl(oldVersion, newVersion, parentConfig) {
44
102
  if (!oldVersion || oldVersion === newVersion) return void 0;
@@ -92,6 +150,8 @@ async function upgradeTemplate(projectDir, options) {
92
150
  };
93
151
  }
94
152
  for (const pkg of packages) if (pkg.from !== void 0 && pkg.from !== pkg.to) updatePackageVersion(projectDir, pkg.name, pkg.to);
153
+ const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);
154
+ for (const pkgPath of workspacePkgPaths) for (const pkg of packages) if (pkg.from !== void 0 && pkg.from !== pkg.to) updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);
95
155
  if (hasUpdates && !options.noInstall) await runBunInstall(projectDir);
96
156
  let syncResult;
97
157
  if (!options.noSync) syncResult = await syncTemplate(projectDir, {
@@ -1 +1 @@
1
- {"version":3,"file":"upgrade.mjs","names":[],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport type { UpgradeOptions, UpgradeResult } from \"../contract\";\nimport { runBunInstall } from \"./init\";\nimport { syncTemplate } from \"./sync\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\ninterface NpmPackageInfo {\n version: string;\n}\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as NpmPackageInfo;\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction updatePackageVersion(projectDir: string, packageName: string, newVersion: string): void {\n const pkgPath = join(projectDir, \"package.json\");\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n\n if (pkg.dependencies && typeof pkg.dependencies === \"object\") {\n const deps = pkg.dependencies as Record<string, string>;\n if (deps[packageName] !== undefined) {\n deps[packageName] = `^${newVersion}`;\n }\n }\n\n if (pkg.devDependencies && typeof pkg.devDependencies === \"object\") {\n const deps = pkg.devDependencies as Record<string, string>;\n if (deps[packageName] !== undefined) {\n deps[packageName] = `^${newVersion}`;\n }\n }\n\n writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\\n`);\n}\n\nfunction buildChangelogUrl(\n oldVersion: string | undefined,\n newVersion: string,\n parentConfig: Record<string, unknown> | null,\n): string | undefined {\n if (!oldVersion || oldVersion === newVersion) return undefined;\n const repoUrl = parentConfig?.repository as string | undefined;\n if (!repoUrl) return undefined;\n\n const githubMatch = repoUrl.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (!githubMatch) return undefined;\n\n const [, owner, repo] = githubMatch;\n return `https://github.com/${owner}/${repo}/compare/v${oldVersion}...v${newVersion}`;\n}\n\nexport async function upgradeTemplate(\n projectDir: string,\n options: UpgradeOptions,\n): Promise<UpgradeResult> {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return {\n status: \"error\",\n packages: [],\n error: \"No package.json found in current directory\",\n };\n }\n\n const packages: UpgradeResult[\"packages\"] = [];\n\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n\n if (!latest) {\n packages.push({ name, from: installed, to: installed ?? \"unknown\" });\n continue;\n }\n\n packages.push({ name, from: installed, to: latest });\n }\n\n const hasUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);\n\n if (options.dryRun) {\n let changelogUrl: string | undefined;\n if (hasUpdates) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n }\n\n return {\n status: \"dry-run\",\n packages,\n changelogUrl,\n };\n }\n\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersion(projectDir, pkg.name, pkg.to);\n }\n }\n\n if (hasUpdates && !options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n let syncResult: UpgradeResult[\"sync\"];\n if (!options.noSync) {\n syncResult = await syncTemplate(projectDir, {\n dryRun: false,\n force: options.force,\n noInstall: true,\n });\n }\n\n let changelogUrl: string | undefined;\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n\n return {\n status: \"upgraded\",\n packages,\n sync: syncResult,\n changelogUrl,\n };\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"upgrade.mjs","names":[],"sources":["../../src/cli/upgrade.ts"],"sourcesContent":["import { existsSync, readFileSync, statSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { glob } from \"glob\";\nimport type { UpgradeOptions, UpgradeResult } from \"../contract\";\nimport { runBunInstall } from \"./init\";\nimport { syncTemplate } from \"./sync\";\n\nconst FRAMEWORK_PACKAGES = [\"everything-dev\", \"every-plugin\"];\n\ninterface NpmPackageInfo {\n version: string;\n}\n\nasync function fetchLatestNpmVersion(packageName: string): Promise<string | null> {\n try {\n const response = await fetch(`https://registry.npmjs.org/${packageName}/latest`, {\n headers: { Accept: \"application/json\" },\n signal: AbortSignal.timeout(10_000),\n });\n if (!response.ok) return null;\n const data = (await response.json()) as NpmPackageInfo;\n return data.version;\n } catch {\n return null;\n }\n}\n\nfunction readInstalledVersion(projectDir: string, packageName: string): string | undefined {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) return undefined;\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\")) as Record<string, unknown>;\n const deps = (pkg.dependencies ?? {}) as Record<string, string>;\n const devDeps = (pkg.devDependencies ?? {}) as Record<string, string>;\n const version = deps[packageName] || devDeps[packageName];\n if (!version) return undefined;\n return version.replace(/^[\\^~>=]+/, \"\");\n}\n\nfunction isBumpedableVersion(value: string | undefined): boolean {\n if (!value) return false;\n if (value === \"workspace:*\") return false;\n if (value.startsWith(\"catalog:\")) return false;\n return true;\n}\n\nfunction bumpDepField(\n field: Record<string, string> | undefined,\n packageName: string,\n newVersion: string,\n): boolean {\n if (!field) return false;\n if (!(packageName in field)) return false;\n const current = field[packageName];\n if (!isBumpedableVersion(current)) return false;\n field[packageName] = `^${newVersion}`;\n return true;\n}\n\nfunction bumpCatalog(catalog: Record<string, string> | undefined, packageName: string, newVersion: string): boolean {\n if (!catalog) return false;\n if (!(packageName in catalog)) return false;\n const current = catalog[packageName];\n if (!isBumpedableVersion(current)) return false;\n catalog[packageName] = `^${newVersion}`;\n return true;\n}\n\ninterface BumpResult {\n modified: boolean;\n fields: string[];\n}\n\nfunction bumpPackageJson(pkg: Record<string, unknown>, packageName: string, newVersion: string): BumpResult {\n const fields: string[] = [];\n\n for (const fieldName of [\"dependencies\", \"devDependencies\", \"peerDependencies\"] as const) {\n const field = pkg[fieldName] as Record<string, string> | undefined;\n if (bumpDepField(field, packageName, newVersion)) {\n fields.push(fieldName);\n }\n }\n\n const workspaces = pkg.workspaces as { catalog?: Record<string, string> } | undefined;\n if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) {\n fields.push(\"workspaces.catalog\");\n }\n\n return { modified: fields.length > 0, fields };\n}\n\nfunction updatePackageVersionInFile(filePath: string, packageName: string, newVersion: string): boolean {\n const pkg = JSON.parse(readFileSync(filePath, \"utf-8\")) as Record<string, unknown>;\n const result = bumpPackageJson(pkg, packageName, newVersion);\n if (result.modified) {\n writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\\n`);\n }\n return result.modified;\n}\n\nfunction updatePackageVersion(projectDir: string, packageName: string, newVersion: string): boolean {\n return updatePackageVersionInFile(join(projectDir, \"package.json\"), packageName, newVersion);\n}\n\nasync function findWorkspacePackageJsons(projectDir: string): Promise<string[]> {\n const rootPkgPath = join(projectDir, \"package.json\");\n if (!existsSync(rootPkgPath)) return [];\n\n const rootPkg = JSON.parse(readFileSync(rootPkgPath, \"utf-8\")) as Record<string, unknown>;\n const workspaceConfig = rootPkg.workspaces as { packages?: string[] } | string[] | undefined;\n\n const patterns: string[] = [];\n if (Array.isArray(workspaceConfig)) {\n patterns.push(...workspaceConfig);\n } else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) {\n patterns.push(...workspaceConfig.packages);\n }\n\n if (patterns.length === 0) return [];\n\n const pkgPaths: string[] = [];\n for (const pattern of patterns) {\n const matches = await glob(pattern, { cwd: projectDir, dot: false, absolute: false });\n for (const match of matches) {\n const pkgPath = join(projectDir, match, \"package.json\");\n if (existsSync(pkgPath) && statSync(pkgPath).isFile()) {\n pkgPaths.push(pkgPath);\n }\n }\n }\n\n return [...new Set(pkgPaths)];\n}\n\nfunction buildChangelogUrl(\n oldVersion: string | undefined,\n newVersion: string,\n parentConfig: Record<string, unknown> | null,\n): string | undefined {\n if (!oldVersion || oldVersion === newVersion) return undefined;\n const repoUrl = parentConfig?.repository as string | undefined;\n if (!repoUrl) return undefined;\n\n const githubMatch = repoUrl.match(/^https?:\\/\\/github\\.com\\/([^/]+)\\/([^/]+?)(?:\\.git)?$/);\n if (!githubMatch) return undefined;\n\n const [, owner, repo] = githubMatch;\n return `https://github.com/${owner}/${repo}/compare/v${oldVersion}...v${newVersion}`;\n}\n\nexport async function upgradeTemplate(\n projectDir: string,\n options: UpgradeOptions,\n): Promise<UpgradeResult> {\n const pkgPath = join(projectDir, \"package.json\");\n if (!existsSync(pkgPath)) {\n return {\n status: \"error\",\n packages: [],\n error: \"No package.json found in current directory\",\n };\n }\n\n const packages: UpgradeResult[\"packages\"] = [];\n\n for (const name of FRAMEWORK_PACKAGES) {\n const installed = readInstalledVersion(projectDir, name);\n const latest = await fetchLatestNpmVersion(name);\n\n if (!latest) {\n packages.push({ name, from: installed, to: installed ?? \"unknown\" });\n continue;\n }\n\n packages.push({ name, from: installed, to: latest });\n }\n\n const hasUpdates = packages.some((p) => p.from !== p.to && p.from !== undefined);\n\n if (options.dryRun) {\n let changelogUrl: string | undefined;\n if (hasUpdates) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n }\n\n return {\n status: \"dry-run\",\n packages,\n changelogUrl,\n };\n }\n\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersion(projectDir, pkg.name, pkg.to);\n }\n }\n\n const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);\n for (const pkgPath of workspacePkgPaths) {\n for (const pkg of packages) {\n if (pkg.from !== undefined && pkg.from !== pkg.to) {\n updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);\n }\n }\n }\n\n if (hasUpdates && !options.noInstall) {\n await runBunInstall(projectDir);\n }\n\n let syncResult: UpgradeResult[\"sync\"];\n if (!options.noSync) {\n syncResult = await syncTemplate(projectDir, {\n dryRun: false,\n force: options.force,\n noInstall: true,\n });\n }\n\n let changelogUrl: string | undefined;\n const mainPkg = packages.find((p) => p.name === \"everything-dev\");\n if (mainPkg?.from && mainPkg.from !== mainPkg.to) {\n const configPath = join(projectDir, \"bos.config.json\");\n let parentConfig: Record<string, unknown> | null = null;\n if (existsSync(configPath)) {\n try {\n parentConfig = JSON.parse(readFileSync(configPath, \"utf-8\"));\n } catch {}\n }\n changelogUrl = buildChangelogUrl(mainPkg.from, mainPkg.to, parentConfig);\n }\n\n return {\n status: \"upgraded\",\n packages,\n sync: syncResult,\n changelogUrl,\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "everything-dev",
3
- "version": "1.7.0",
3
+ "version": "1.7.2",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -152,8 +152,7 @@
152
152
  "gradient-string": "^3.0.0",
153
153
  "hono": "^4.7.11",
154
154
  "ink": "^6.8.0",
155
- "tar": "^7.4.3",
156
- "zod": "^4.3.6"
155
+ "tar": "^7.4.3"
157
156
  },
158
157
  "peerDependencies": {
159
158
  "@tanstack/react-query": ">=5.0.0",
@@ -1,5 +1,6 @@
1
- import { existsSync, readFileSync, writeFileSync } from "node:fs";
1
+ import { existsSync, readFileSync, statSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
+ import { glob } from "glob";
3
4
  import type { UpgradeOptions, UpgradeResult } from "../contract";
4
5
  import { runBunInstall } from "./init";
5
6
  import { syncTemplate } from "./sync";
@@ -35,25 +36,99 @@ function readInstalledVersion(projectDir: string, packageName: string): string |
35
36
  return version.replace(/^[\^~>=]+/, "");
36
37
  }
37
38
 
38
- function updatePackageVersion(projectDir: string, packageName: string, newVersion: string): void {
39
- const pkgPath = join(projectDir, "package.json");
40
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8")) as Record<string, unknown>;
39
+ function isBumpedableVersion(value: string | undefined): boolean {
40
+ if (!value) return false;
41
+ if (value === "workspace:*") return false;
42
+ if (value.startsWith("catalog:")) return false;
43
+ return true;
44
+ }
45
+
46
+ function bumpDepField(
47
+ field: Record<string, string> | undefined,
48
+ packageName: string,
49
+ newVersion: string,
50
+ ): boolean {
51
+ if (!field) return false;
52
+ if (!(packageName in field)) return false;
53
+ const current = field[packageName];
54
+ if (!isBumpedableVersion(current)) return false;
55
+ field[packageName] = `^${newVersion}`;
56
+ return true;
57
+ }
58
+
59
+ function bumpCatalog(catalog: Record<string, string> | undefined, packageName: string, newVersion: string): boolean {
60
+ if (!catalog) return false;
61
+ if (!(packageName in catalog)) return false;
62
+ const current = catalog[packageName];
63
+ if (!isBumpedableVersion(current)) return false;
64
+ catalog[packageName] = `^${newVersion}`;
65
+ return true;
66
+ }
41
67
 
42
- if (pkg.dependencies && typeof pkg.dependencies === "object") {
43
- const deps = pkg.dependencies as Record<string, string>;
44
- if (deps[packageName] !== undefined) {
45
- deps[packageName] = `^${newVersion}`;
68
+ interface BumpResult {
69
+ modified: boolean;
70
+ fields: string[];
71
+ }
72
+
73
+ function bumpPackageJson(pkg: Record<string, unknown>, packageName: string, newVersion: string): BumpResult {
74
+ const fields: string[] = [];
75
+
76
+ for (const fieldName of ["dependencies", "devDependencies", "peerDependencies"] as const) {
77
+ const field = pkg[fieldName] as Record<string, string> | undefined;
78
+ if (bumpDepField(field, packageName, newVersion)) {
79
+ fields.push(fieldName);
46
80
  }
47
81
  }
48
82
 
49
- if (pkg.devDependencies && typeof pkg.devDependencies === "object") {
50
- const deps = pkg.devDependencies as Record<string, string>;
51
- if (deps[packageName] !== undefined) {
52
- deps[packageName] = `^${newVersion}`;
83
+ const workspaces = pkg.workspaces as { catalog?: Record<string, string> } | undefined;
84
+ if (workspaces?.catalog && bumpCatalog(workspaces.catalog, packageName, newVersion)) {
85
+ fields.push("workspaces.catalog");
86
+ }
87
+
88
+ return { modified: fields.length > 0, fields };
89
+ }
90
+
91
+ function updatePackageVersionInFile(filePath: string, packageName: string, newVersion: string): boolean {
92
+ const pkg = JSON.parse(readFileSync(filePath, "utf-8")) as Record<string, unknown>;
93
+ const result = bumpPackageJson(pkg, packageName, newVersion);
94
+ if (result.modified) {
95
+ writeFileSync(filePath, `${JSON.stringify(pkg, null, 2)}\n`);
96
+ }
97
+ return result.modified;
98
+ }
99
+
100
+ function updatePackageVersion(projectDir: string, packageName: string, newVersion: string): boolean {
101
+ return updatePackageVersionInFile(join(projectDir, "package.json"), packageName, newVersion);
102
+ }
103
+
104
+ async function findWorkspacePackageJsons(projectDir: string): Promise<string[]> {
105
+ const rootPkgPath = join(projectDir, "package.json");
106
+ if (!existsSync(rootPkgPath)) return [];
107
+
108
+ const rootPkg = JSON.parse(readFileSync(rootPkgPath, "utf-8")) as Record<string, unknown>;
109
+ const workspaceConfig = rootPkg.workspaces as { packages?: string[] } | string[] | undefined;
110
+
111
+ const patterns: string[] = [];
112
+ if (Array.isArray(workspaceConfig)) {
113
+ patterns.push(...workspaceConfig);
114
+ } else if (workspaceConfig?.packages && Array.isArray(workspaceConfig.packages)) {
115
+ patterns.push(...workspaceConfig.packages);
116
+ }
117
+
118
+ if (patterns.length === 0) return [];
119
+
120
+ const pkgPaths: string[] = [];
121
+ for (const pattern of patterns) {
122
+ const matches = await glob(pattern, { cwd: projectDir, dot: false, absolute: false });
123
+ for (const match of matches) {
124
+ const pkgPath = join(projectDir, match, "package.json");
125
+ if (existsSync(pkgPath) && statSync(pkgPath).isFile()) {
126
+ pkgPaths.push(pkgPath);
127
+ }
53
128
  }
54
129
  }
55
130
 
56
- writeFileSync(pkgPath, `${JSON.stringify(pkg, null, 2)}\n`);
131
+ return [...new Set(pkgPaths)];
57
132
  }
58
133
 
59
134
  function buildChangelogUrl(
@@ -130,6 +205,15 @@ export async function upgradeTemplate(
130
205
  }
131
206
  }
132
207
 
208
+ const workspacePkgPaths = await findWorkspacePackageJsons(projectDir);
209
+ for (const pkgPath of workspacePkgPaths) {
210
+ for (const pkg of packages) {
211
+ if (pkg.from !== undefined && pkg.from !== pkg.to) {
212
+ updatePackageVersionInFile(pkgPath, pkg.name, pkg.to);
213
+ }
214
+ }
215
+ }
216
+
133
217
  if (hasUpdates && !options.noInstall) {
134
218
  await runBunInstall(projectDir);
135
219
  }